diff --git a/docs/en/picture.md b/docs/en/picture.md
index 85775e381..ce403ab7e 100644
--- a/docs/en/picture.md
+++ b/docs/en/picture.md
@@ -196,6 +196,42 @@ For example:
+## wallhaven
+
+::: tip Tip
+
+When parameter **Need Details** is set to `true` `yes` `t` `y`, RSS will add the title, uploader, upload time, and category information of each image, which can support the filtering function of RSS reader.
+
+However, the number of requests to the site increases a lot when it is turned on, which causes the site to return `Response code 429 (Too Many Requests)`. So you need to specify a smaller `limit` parameter, i.e. add `?limit=` after the route, here is an example.
+
+For example [Latest Wallpapers](https://wallhaven.cc/latest), the route turning on **Need Details** is [/wallhaven/latest/true](https://rsshub.app/wallhaven/latest/true), and then specify a smaller `limit`. We can get [/wallhaven/latest/true?limit=5](https://rsshub.app/wallhaven/latest/true?limit=5).
+
+:::
+
+### Category
+
+
+
+| Latest | Hot | Toplist | Random |
+| ------ | --- | ------- | ------ |
+| latest | hot | toplist | random |
+
+
+
+### Search
+
+
+
+::: tip Tip
+
+Subscribe pages starting with `https://wallhaven.cc/search`, fill the text after `?` as `filter` in the route. The following is an example:
+
+The text after `?` is `q=id%3A711&sorting=random&ref=fp&seed=8g0dgd` for [Wallpaper Search: #landscape - wallhaven.cc](https://wallhaven.cc/search?q=id%3A711&sorting=random&ref=fp&seed=8g0dgd), so the route is [/wallhaven/q=id%3A711&sorting=random&ref=fp&seed=8g0dgd](https://rsshub.app/wallhaven/q=id%3A711&sorting=random&ref=fp&seed=8g0dgd)
+
+:::
+
+
+
## yande.re
::: tip
diff --git a/docs/picture.md b/docs/picture.md
index 32bad3423..2cbf4b6e3 100644
--- a/docs/picture.md
+++ b/docs/picture.md
@@ -358,6 +358,42 @@ R18 显示
+## wallhaven
+
+::: tip 提示
+
+参数 **需要图片信息** 设置为 `true` `yes` `t` `y` 等值后,RSS 会携带各图片的标题、上传者、上传时间、分类信息,可支持 RSS 阅读器的筛选功能。
+
+但开启后对该网站请求次数大量增多,从而导致网站返回 `Response code 429 (Too Many Requests)`。所以需要指定更小的 `limit` 参数值,即在路由后添加 `?limit=<单次请求获取数目>`,下面是一个例子:
+
+如 [Latest Wallpapers](https://wallhaven.cc/latest),开启 **需要图片信息** 后的路由为 [/wallhaven/latest/true](https://rsshub.app/wallhaven/latest/true),此时再指定更小的 `limit` 参数值,即 [/wallhaven/latest/true?limit=5](https://rsshub.app/wallhaven/latest/true?limit=5)
+
+:::
+
+### 分类
+
+
+
+| Latest | Hot | Toplist | Random |
+| ------ | --- | ------- | ------ |
+| latest | hot | toplist | random |
+
+
+
+### 搜索
+
+
+
+::: tip 提示
+
+订阅以 `https://wallhaven.cc/search` 起始的页面,将 `?` 后的字段作为 `filter` 填写入路由。下面是一个例子:
+
+如 [Wallpaper Search: #landscape - wallhaven.cc](https://wallhaven.cc/search?q=id%3A711&sorting=random&ref=fp&seed=8g0dgd),中 `?` 后的字段为 `q=id%3A711&sorting=random&ref=fp&seed=8g0dgd`,所以可以得到路由是 [/wallhaven/search/q=id%3A711&sorting=random&ref=fp&seed=8g0dgd](https://rsshub.app/wallhaven/search/q=id%3A711&sorting=random&ref=fp&seed=8g0dgd)
+
+:::
+
+
+
## Wallpaperhub
diff --git a/lib/v2/wallhaven/index.js b/lib/v2/wallhaven/index.js
new file mode 100644
index 000000000..c0044b8b3
--- /dev/null
+++ b/lib/v2/wallhaven/index.js
@@ -0,0 +1,55 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+const rootUrl = 'https://wallhaven.cc';
+
+module.exports = async (ctx) => {
+ const filter = ctx.params.filter ?? 'latest';
+ const needDetails = /t|y/i.test(ctx.params.needDetails ?? 'false');
+ const url = `${rootUrl}/${filter.indexOf('=') > 0 ? `search?${filter.replace(/page=\d+/g, 'page=1')}` : filter}`;
+
+ const response = await got.get(url);
+ const $ = cheerio.load(response.data);
+
+ let items = $('li > figure.thumb')
+ .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 24)
+ .map((_, item) => ({
+ title: $(item).find('img.lazyload').attr('data-src').split('/').pop(),
+ description: $(item)
+ .html()
+ .match(//)[0],
+ link: $(item).find('a.preview').attr('href'),
+ }))
+ .get();
+ if (needDetails) {
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got({
+ method: 'get',
+ url: item.link,
+ });
+
+ const content = cheerio.load(detailResponse.data);
+
+ item.title = content('meta[name="title"]').attr('content');
+ item.author = content('.username').text();
+ item.pubDate = parseDate(content('time').attr('datetime'));
+ item.category = content('.tagname')
+ .toArray()
+ .map((tag) => content(tag).text());
+ item.description = content('div.scrollbox').html();
+
+ return item;
+ })
+ )
+ );
+ }
+
+ ctx.state.data = {
+ title: $('title').text(),
+ link: url,
+ item: items,
+ };
+};
diff --git a/lib/v2/wallhaven/maintainer.js b/lib/v2/wallhaven/maintainer.js
new file mode 100644
index 000000000..23c26b65f
--- /dev/null
+++ b/lib/v2/wallhaven/maintainer.js
@@ -0,0 +1,4 @@
+module.exports = {
+ '/search/:filter?/:needDetails?': ['nczitzk', 'Fatpandac'],
+ '/:filter?/:needDetails?': ['nczitzk', 'Fatpandac'],
+};
diff --git a/lib/v2/wallhaven/radar.js b/lib/v2/wallhaven/radar.js
new file mode 100644
index 000000000..e06232b85
--- /dev/null
+++ b/lib/v2/wallhaven/radar.js
@@ -0,0 +1,53 @@
+module.exports = {
+ 'wallhaven.cc': {
+ _name: 'wallhaven',
+ '.': [
+ {
+ title: 'Latest',
+ docs: 'https://docs.rsshub.app/picture.html#wallhaven-zhu-zhu-ti',
+ source: ['/:category', '/'],
+ target: (params) => {
+ if (params.category === 'latest') {
+ return '/wallhaven/latest';
+ }
+ },
+ },
+ {
+ title: 'Hot',
+ docs: 'https://docs.rsshub.app/picture.html#wallhaven-zhu-zhu-ti',
+ source: ['/:category', '/'],
+ target: (params) => {
+ if (params.category === 'hot') {
+ return '/wallhaven/hot';
+ }
+ },
+ },
+ {
+ title: 'TopList',
+ docs: 'https://docs.rsshub.app/picture.html#wallhaven-zhu-zhu-ti',
+ source: ['/:category', '/'],
+ target: (params) => {
+ if (params.category === 'toplist') {
+ return '/wallhaven/toplist';
+ }
+ },
+ },
+ {
+ title: 'Random',
+ docs: 'https://docs.rsshub.app/picture.html#wallhaven-zhu-zhu-ti',
+ source: ['/:category', '/'],
+ target: (params) => {
+ if (params.category === 'random') {
+ return '/wallhaven/random';
+ }
+ },
+ },
+ {
+ title: 'Search',
+ docs: 'https://docs.rsshub.app/picture.html#wallhaven-sou-xiao-sou-shao-suo',
+ source: ['/'],
+ target: '/wallhaven/search/:filter?/:needDetails?',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/wallhaven/router.js b/lib/v2/wallhaven/router.js
new file mode 100644
index 000000000..d649e36cf
--- /dev/null
+++ b/lib/v2/wallhaven/router.js
@@ -0,0 +1,4 @@
+module.exports = function (router) {
+ router.get('/search/:filter?/:needDetails?', require('./index'));
+ router.get('/:filter?/:needDetails?', require('./index'));
+};