diff --git a/docs/en/other.md b/docs/en/other.md
index 5ecd0c63c..00306cb50 100644
--- a/docs/en/other.md
+++ b/docs/en/other.md
@@ -238,3 +238,13 @@ Type
| all | rec |
+
+## 裏垢女子まとめ
+
+### Homepage
+
+
+
+### User
+
+
diff --git a/docs/other.md b/docs/other.md
index a384dda4f..be1f00bfb 100644
--- a/docs/other.md
+++ b/docs/other.md
@@ -613,11 +613,11 @@ type 为 all 时,category 参数不支持 cost 和 free
### 主页
-
+
### 用户
-
+
## 律师事务所文章
diff --git a/lib/router.js b/lib/router.js
index fe938fdbc..6c60021c1 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -1378,9 +1378,9 @@ router.get('/blogs/wang54/:id?', lazyloadRouteHandler('./routes/blogs/wang54'));
// WordPress
router.get('/blogs/wordpress/:domain/:https?', lazyloadRouteHandler('./routes/blogs/wordpress'));
-// 裏垢女子まとめ
-router.get('/uraaka-joshi', lazyloadRouteHandler('./routes/uraaka-joshi/uraaka-joshi'));
-router.get('/uraaka-joshi/:id', lazyloadRouteHandler('./routes/uraaka-joshi/uraaka-joshi-user'));
+// 裏垢女子まとめ migrated to v2
+// router.get('/uraaka-joshi', lazyloadRouteHandler('./routes/uraaka-joshi/uraaka-joshi'));
+// router.get('/uraaka-joshi/:id', lazyloadRouteHandler('./routes/uraaka-joshi/uraaka-joshi-user'));
// 西祠胡同
router.get('/xici/:id?', lazyloadRouteHandler('./routes/xici'));
diff --git a/lib/routes/uraaka-joshi/uraaka-joshi-user.js b/lib/routes/uraaka-joshi/uraaka-joshi-user.js
deleted file mode 100644
index fab365311..000000000
--- a/lib/routes/uraaka-joshi/uraaka-joshi-user.js
+++ /dev/null
@@ -1,28 +0,0 @@
-const buildData = require('@/utils/common-config');
-
-module.exports = async (ctx) => {
- const params = ctx.params;
- const link = `https://www.uraaka-joshi.com/users/${params.id}`;
- ctx.state.data = await buildData({
- link,
- url: link,
- title: `$('.top-profile-card-name-link').text() + '@${params.id} - 裏垢女子まとめ'`,
- item: {
- item: '.content-main .stream .stream-item .post',
- title: `$('.post-name').text() + '@${params.id} - 裏垢女子まとめ'`,
- link: `https://www.uraaka-joshi.com/users/${params.id}`,
- description: `$('.context *').removeAttr('onclick');
- $('.context *').removeAttr('onerror');
- $('.context *').removeAttr('style');
- $('.context video').each((video) => {
- const poster = $(video).attr('poster')
- const src = $(video).attr('src')
- $(video).attr('poster', 'https:' + poster)
- $(video).attr('src', 'https:' + src)
- });
- $('.context').html();`,
- pubDate: `new Date($('.post-time').attr('datetime')).toUTCString()`,
- guid: `new Date($('.post-time').attr('datetime')).getTime()`,
- },
- });
-};
diff --git a/lib/routes/uraaka-joshi/uraaka-joshi.js b/lib/routes/uraaka-joshi/uraaka-joshi.js
deleted file mode 100644
index aa3509c67..000000000
--- a/lib/routes/uraaka-joshi/uraaka-joshi.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const buildData = require('@/utils/common-config');
-
-module.exports = async (ctx) => {
- const link = `https://www.uraaka-joshi.com/`;
- ctx.state.data = await buildData({
- link,
- url: link,
- title: `%title%`,
- params: {
- title: '裏垢女子まとめ',
- },
- item: {
- item: '.content-main .stream .stream-item .post',
- title: `$('.post-account-group').text() + ' - %title%'`,
- link: `$('.post-account-group').attr('href')`,
- description: `$('.context *').removeAttr('onclick');
- $('.context *').removeAttr('onerror');
- $('.context *').removeAttr('style');
- $('.context video').each((video) => {
- const poster = $(video).attr('poster')
- const src = $(video).attr('src')
- $(video).attr('poster', 'https:' + poster)
- $(video).attr('src', 'https:' + src)
- });
- $('.context').html();`,
- pubDate: `new Date($('.post-time').attr('datetime')).toUTCString()`,
- guid: `new Date($('.post-time').attr('datetime')).getTime()`,
- },
- });
-};
diff --git a/lib/v2/uraaka-joshi/maintainer.js b/lib/v2/uraaka-joshi/maintainer.js
new file mode 100644
index 000000000..7b2bbf67d
--- /dev/null
+++ b/lib/v2/uraaka-joshi/maintainer.js
@@ -0,0 +1,4 @@
+module.exports = {
+ '/': ['SettingDust', 'Halcao'],
+ '/:id': ['SettingDust', 'Halcao'],
+};
diff --git a/lib/v2/uraaka-joshi/radar.js b/lib/v2/uraaka-joshi/radar.js
new file mode 100644
index 000000000..07285e081
--- /dev/null
+++ b/lib/v2/uraaka-joshi/radar.js
@@ -0,0 +1,19 @@
+module.exports = {
+ 'uraaka-joshi.com': {
+ _name: '裏垢女子まとめ',
+ '.': [
+ {
+ title: '主页',
+ docs: 'https://docs-rsshub.pages.dev/other.html#li-gou-nu-zi-まとめ',
+ source: ['/'],
+ target: '/uraaka-joshi',
+ },
+ {
+ title: '用户',
+ docs: 'https://docs-rsshub.pages.dev/other.html#li-gou-nu-zi-まとめ',
+ source: ['/:id'],
+ target: '/uraaka-joshi/:id',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/uraaka-joshi/router.js b/lib/v2/uraaka-joshi/router.js
new file mode 100644
index 000000000..f4c4775db
--- /dev/null
+++ b/lib/v2/uraaka-joshi/router.js
@@ -0,0 +1,4 @@
+module.exports = (router) => {
+ router.get('/', require('./uraaka-joshi'));
+ router.get('/:id', require('./uraaka-joshi-user'));
+};
diff --git a/lib/v2/uraaka-joshi/uraaka-joshi-user.js b/lib/v2/uraaka-joshi/uraaka-joshi-user.js
new file mode 100644
index 000000000..8f6ede029
--- /dev/null
+++ b/lib/v2/uraaka-joshi/uraaka-joshi-user.js
@@ -0,0 +1,89 @@
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const config = require('@/config').value;
+
+module.exports = async (ctx) => {
+ const { id } = ctx.params;
+ const link = `https://www.uraaka-joshi.com/user/${id}`;
+
+ const response = await ctx.cache.tryGet(
+ link,
+ async () => {
+ const browser = await require('@/utils/puppeteer')();
+ const page = await browser.newPage();
+
+ await page.goto(link);
+ await page.waitForSelector('#pickup03 .grid-cell');
+ await page.waitForSelector('#pickup04 .grid-cell');
+ await page.waitForSelector('#main-block .grid-cell');
+
+ const html = await page.evaluate(() => document.documentElement.innerHTML);
+ browser.close();
+ return html;
+ },
+ config.cache.routeExpire,
+ false
+ );
+
+ const $ = cheerio.load(response);
+ const items = $('#main-block .grid .grid-cell')
+ .toArray()
+ .map((item) => {
+ item = $(item);
+
+ // remove event and styles
+ item.find('*').removeAttr('onclick');
+ item.find('*').removeAttr('onerror');
+ item.find('*').removeAttr('style');
+
+ // format account style
+ const account = item.find('.account-group-link-row');
+ account.html(account.text());
+
+ // extract video tag from its player
+ item.find('.plyr--video').each((_, player) => {
+ player = $(player);
+
+ const v = player.find('video');
+ player.replaceWith(v);
+ v.attr('poster', 'https:' + v.attr('data-poster'));
+ v.find('source').attr('src', 'https:' + v.find('source').attr('src'));
+ });
+
+ // correct src of img tags
+ item.find('img').each((_, i) => {
+ i = $(i);
+ i.attr('src', 'https:' + i.attr('data-src').split('?resize')[0]);
+ i.removeAttr('data-src');
+ });
+
+ const author = item.find('.account-group').text();
+ const categories = item
+ .find('.hashtag-item .hashtag')
+ .toArray()
+ .map((c) => $(c).text().trim());
+ const link = item.find('.account-group-link-row').attr('href');
+ const date = parseDate(item.find('.profile-char').attr('datetime'));
+ const guid = item.find('a.tap-image').attr('data-tweet-id') || item.find('video[class^="js-player-"]').attr('data-tweet-id');
+
+ item.find('.grow-room').remove();
+ item.find('div.profile-group.mt10.prl2').eq(1).remove();
+
+ return {
+ title: item.find('.profile-text').text(),
+ description: item.html(),
+ link,
+ pubDate: date,
+ guid,
+ category: categories,
+ author,
+ };
+ });
+
+ ctx.state.data = {
+ title: $('title').text(),
+ description: $('meta[name="description"]').attr('content'),
+ link,
+ item: items,
+ };
+};
diff --git a/lib/v2/uraaka-joshi/uraaka-joshi.js b/lib/v2/uraaka-joshi/uraaka-joshi.js
new file mode 100644
index 000000000..7c657fde4
--- /dev/null
+++ b/lib/v2/uraaka-joshi/uraaka-joshi.js
@@ -0,0 +1,71 @@
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+module.exports = async (ctx) => {
+ const link = `https://www.uraaka-joshi.com/`;
+ const title = `裏垢女子まとめ`;
+
+ const browser = await require('@/utils/puppeteer')();
+
+ const page = await browser.newPage();
+ await page.goto(link);
+
+ await page.waitForSelector('#pickup03 .grid-cell');
+ await page.waitForSelector('#pickup04 .grid-cell');
+ await page.waitForSelector('#main-block .grid-cell');
+
+ const bodyHandle = await page.$('body');
+ const html = await page.evaluate((body) => body.innerHTML, bodyHandle);
+ browser.close();
+ const $ = cheerio.load(html);
+ const list = $('.grid-cell');
+
+ ctx.state.data = {
+ title,
+ link,
+ item:
+ list &&
+ list
+ .map((index, item) => {
+ item = $(item);
+
+ // remove event and styles
+ item.find('*').removeAttr('onclick');
+ item.find('*').removeAttr('onerror');
+ item.find('*').removeAttr('style');
+
+ // format account style
+ const account = item.find('.account-group-link-row');
+ account.html(account.text());
+
+ // extract video tag from its player
+ item.find('.plyr--video').each((_, player) => {
+ player = $(player);
+
+ const video = player.find('video');
+ player.replaceWith(video);
+ const poster = video.attr('data-poster');
+ video.attr('poster', 'https:' + poster);
+
+ const source = video.find('source');
+ const src = source.attr('src');
+ source.attr('src', 'https:' + src);
+ });
+
+ // correct src of img tags
+ item.find('img').each((_, image) => {
+ const src = $(image).attr('data-src');
+ $(image).attr('src', 'https:' + src);
+ });
+
+ return {
+ title: item.find('.account-group').text() + ` - ${title}`,
+ description: item.html(),
+ link: item.find('.account-group-link-row').attr('href'),
+ pubDate: parseDate(item.find('.profile-char').attr('datetime')),
+ guid: item.find('a.tap-image').attr('data-tweet-id') || item.find('video[class^="js-player-"]').attr('data-tweet-id') || parseDate(item.find('.profile-char').attr('datetime')).getTime(),
+ };
+ })
+ .get(),
+ };
+};