diff --git a/docs/en/social-media.md b/docs/en/social-media.md
index a7156f2cc..b607e21fd 100644
--- a/docs/en/social-media.md
+++ b/docs/en/social-media.md
@@ -150,7 +150,7 @@ Type
Due to Instagram Private API restrictions, you have to setup your credentials on the server. 2FA is not supported. See [deployment guide](https://docs.rsshub.app/en/install/) for more.
-If you don't want to setup credentials, you can use [Picuki](#picuki).
+If you don't want to setup credentials, you can use [Picnob](#picnob) or [Picuki](#picuki).
:::
@@ -233,6 +233,12 @@ These feed do not include boosts (a.k.a. reblogs). RSSHub provides a feed for us
+## Picnob
+
+### User Profile
+
+
+
## Picuki
### User Profile
diff --git a/lib/v2/picnob/maintainer.js b/lib/v2/picnob/maintainer.js
new file mode 100644
index 000000000..625e52ce6
--- /dev/null
+++ b/lib/v2/picnob/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/user/:id': ['TonyRL'],
+};
diff --git a/lib/v2/picnob/radar.js b/lib/v2/picnob/radar.js
new file mode 100644
index 000000000..0d043c2e0
--- /dev/null
+++ b/lib/v2/picnob/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'picnob.com': {
+ _name: 'Picnob',
+ '.': [
+ {
+ title: 'User profile',
+ docs: 'https://docs.rsshub.app/en/social-media.html#picnob',
+ source: ['/profile/:id/*'],
+ target: '/picnob/user/:id',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/picnob/router.js b/lib/v2/picnob/router.js
new file mode 100644
index 000000000..a3bdfa517
--- /dev/null
+++ b/lib/v2/picnob/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/user/:id', require('./user'));
+};
diff --git a/lib/v2/picnob/templates/desc.art b/lib/v2/picnob/templates/desc.art
new file mode 100644
index 000000000..a380de17a
--- /dev/null
+++ b/lib/v2/picnob/templates/desc.art
@@ -0,0 +1,13 @@
+{{ if item.type === 'video' }}
+
+{{ else if item.type === 'img_multi' }}
+ {{ each images i }}
+
+ {{ /each }}
+{{ else if item.type === 'img_sig' }}
+
+{{ /if }}
+
+{{@ item.sum }}
diff --git a/lib/v2/picnob/user.js b/lib/v2/picnob/user.js
new file mode 100644
index 000000000..6c35449db
--- /dev/null
+++ b/lib/v2/picnob/user.js
@@ -0,0 +1,61 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const baseUrl = 'https://www.picnob.com';
+ const { id } = ctx.params;
+ const url = `${baseUrl}/profile/${id}/`;
+ const { data: response } = await got(url);
+ const $ = cheerio.load(response);
+
+ const profileName = $('h1.fullname').text();
+ const userId = $('input[name=userid]').attr('value');
+
+ const { data } = await got(`${baseUrl}/api/posts`, {
+ searchParams: {
+ userid: userId,
+ },
+ });
+
+ const list = data.posts.items.map(async (item) => {
+ const { shortcode, type } = item;
+ const link = `${baseUrl}/post/${shortcode}/`;
+ let images = [];
+ if (type === 'img_multi') {
+ images = await ctx.cache.tryGet(link, async () => {
+ const { data } = await got(link);
+ const $ = cheerio.load(data);
+ return [
+ ...new Set(
+ $('.post_slide a')
+ .toArray()
+ .map((a) => {
+ a = $(a);
+ return {
+ ori: a.attr('href'),
+ url: a.find('img').attr('data-src'),
+ };
+ })
+ ),
+ ];
+ });
+ }
+ return {
+ title: item.sum_pure,
+ description: art(path.join(__dirname, 'templates/desc.art'), { item, images }),
+ link,
+ pubDate: parseDate(item.time, 'X'),
+ };
+ });
+
+ ctx.state.data = {
+ title: `${profileName} (@${id}) - Picnob`,
+ description: $('.info .sum').text(),
+ link: url,
+ image: $('.ava .pic img').attr('src'),
+ item: list,
+ };
+};