feat(route): add linkedin.cn route (#11718)

* fix(route): xiaomiyoupin crowdfunding error and migrate to v2

* initial commit

* add searchHit

* add jobPosting

* linkedincn

* add linkedincn ok

* feat(route): 中国新闻周刊 (#11665)

* feat(route): 中国新闻周刊

* fix: decode via iconv instead of puppeteer

* fix: cache full text request

* Update lib/v2/inewsweek/index.js

* fix: exact publish time

* fix: timezone

* feat(route): 修复 HelloGitHub 的 月刊 路由 (#11675)

* feat(route): 添加 百度股市通

* fix: 修改 百度股市通 路径

* docs: 添加 百度股市通 文档

* feat: 添加 radar 支持

* fix: 按字母顺序插入新路由

* feat(route): 新增 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪

* fix: 优化 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 的标题

* fix: 修复 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 部分情况下的非空判断

* fix: 修复 地区名称标题的问题

* fix: 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 的 guid 增加 pubDate

* fix: 修复 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 guid 中添加 pubDate

* fix: 修改 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 的 title

* feat(route): 修复 HelloGitHub 的 月刊 路由

* fix: remove guid

* fix(route): x6d (#11676)

* chore(deps): bump rand-user-agent from 1.0.101 to 1.0.102 (#11678)

Bumps [rand-user-agent](https://github.com/WebScrapingAPI/rand-user-agent) from 1.0.101 to 1.0.102.
- [Release notes](https://github.com/WebScrapingAPI/rand-user-agent/releases)
- [Commits](https://github.com/WebScrapingAPI/rand-user-agent/commits)

---
updated-dependencies:
- dependency-name: rand-user-agent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @sentry/node from 7.31.1 to 7.32.1 (#11680)

Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.31.1 to 7.32.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.31.1...7.32.1)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump jsdom from 21.0.0 to 21.1.0 (#11681)

Bumps [jsdom](https://github.com/jsdom/jsdom) from 21.0.0 to 21.1.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/21.0.0...21.1.0)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump puppeteer from 19.5.2 to 19.6.0 (#11679)

Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 19.5.2 to 19.6.0.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v19.5.2...puppeteer-v19.6.0)

---
updated-dependencies:
- dependency-name: puppeteer
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(route): famitsu (#11682)

* fix(route): xiaomiyoupin crowdfunding error and migrate to v2 (#11677)

* fix(route): xiaomiyoupin crowdfunding error and migrate to v2

* Update lib/v2/xiaomiyoupin/maintainer.js

* Update docs/shopping.md

* use single api endpoint for both module

* add pubDate()

* fix(route): ikea (#11685)

* chore(deps): bump @sentry/node from 7.32.1 to 7.33.0 (#11687)

Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.32.1 to 7.33.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.32.1...7.33.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump jest from 29.3.1 to 29.4.0 (#11686)

Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 29.3.1 to 29.4.0.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.4.0/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(route): bloomberg videopage parse error (#11688)

* fix(route): clickme (#11689)

* fix(route): lofter (#11696)

* fix(route): caixin blog (#11695)

* fix(route): caixin blog

* fix: url

* chore(deps): bump ioredis from 5.2.5 to 5.3.0 (#11697)

Bumps [ioredis](https://github.com/luin/ioredis) from 5.2.5 to 5.3.0.
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/luin/ioredis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/luin/ioredis/compare/v5.2.5...v5.3.0)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(route): bioone (#11699)

* feat(route): github wiki history (#11700)

* feat(route): wuhan (#11701)

Co-authored-by: nczitzk <nczitzk@gmail.com>

* chore(deps): bump puppeteer from 19.6.0 to 19.6.1 (#11702)

Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 19.6.0 to 19.6.1.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v19.6.0...puppeteer-v19.6.1)

---
updated-dependencies:
- dependency-name: puppeteer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump jest from 29.4.0 to 29.4.1 (#11703)

Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 29.4.0 to 29.4.1.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.4.1/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(route): fix date err and refactor to v2 (#11705)

* fix(route): fix date err and refactor to v2

* docs: add en docs

* fix: typo

* feat(route): add mixcloud (#11672)

* feat(route): add mixcloud

* docs

* feat(route): add mixcloud

* convert linebreaks to <br>

* docs: add supportPodcast

* codestyle: use template literals

* refactor: split graph ql queries

* fix(route): 软餐 (#11708)

* feat(route): gelonghui (#11710)

* chore(deps): bump @sentry/node from 7.33.0 to 7.34.0 (#11711)

Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.33.0 to 7.34.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.33.0...7.34.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(route): fix not unique guid and refactor to v2 (#11712)

* add null check

* add more null check for compensation

* remove unused key

* refactor: change route

* fix typo

---------
This commit is contained in:
Felix Hsu
2023-01-30 17:41:58 +08:00
committed by GitHub
co-authored by dependabot[bot]
parent d1b7b08e27
commit 304c84a0d1
7 changed files with 197 additions and 1 deletions
+23
View File
@@ -163,6 +163,29 @@ pageClass: routes
该网站原始 RSS 数据源无人维护,故重新抓取数据并生成数据源。
## LinkedIn 领英中国
### Jobs
<Route author="bigfei" example="/linkedin/cn/jobs/Software" path="/linkedin/cn/jobs/:keywords?" :paramsDesc="['搜索关键字']" radar="1">
另外,可以通过添加额外的以下 query 参数来输出满足特定要求的工作职位:
| 参数 | 描述 | 举例 | 默认值 |
| ---------- | -------------------------------- | ----------------------------------------- | ------- |
| `geo` | geo 编码 | 102890883(中国)、102772228(上海)、103873152(北京) | 空 |
| `remote` | 是否只显示远程工作 | `true/false` | `false` |
| `location` | 工作地点 | `china/shanghai/beijing` | 空 |
| `relevant` | 排序方式 (true: 按相关性排序,false: 按日期排序) | `true/false` | `false` |
| `period` | 发布时间 | `1/7/30` | 空 |
例如:
[`/linkedin/cn/jobs/Software?location=shanghai&period=1`](https://rsshub.app/linkedin/cn/jobs/Software?location=shanghai\&period=1): 查找所有在上海的今日发布的所有 Software 工作
**为了方便起见,建议您在 [LinkedIn.cn](https://www.linkedin.cn/incareer/jobs/search) 上进行搜索,并使用 [RSSHub Radar](https://github.com/DIYgod/RSSHub-Radar) 加载特定的 feed。**
</Route>
## MiniFlux
因需设置 API Key,故请自行架设 RSSHub。API 密钥则请于 MiniFlux 实例中的 `设置` -> `API密钥` -> `创建一个新的API密钥` 处获取。
+13
View File
@@ -0,0 +1,13 @@
const { parseSearchHit, parseJobPosting } = require('./utils');
const siteUrl = 'https://www.linkedin.cn/incareer/jobs/search';
module.exports = async (ctx) => {
const { title, jobs } = await parseSearchHit(ctx);
const items = await Promise.all(jobs.map((job) => parseJobPosting(job)));
ctx.state.data = {
title: `领英 - ${title}`,
link: siteUrl,
item: items,
};
};
+119
View File
@@ -0,0 +1,119 @@
const crypto = require('crypto');
const path = require('path');
const { art } = require('@/utils/render');
const got = require('@/utils/got');
const apiUrl = 'https://www.linkedin.cn/karpos/api/graphql';
const searchHitQueryId = 'searchSearchHitsByJob.be362cd720abd0ebf89b4bbc3253047f';
const jobPostingQueryId = 'jobsJobPostingsById.3b9573e88687a86607ddb74ff013ef50';
const makeHeader = () => {
const sessionId = crypto.randomBytes(8).toString('hex').slice(0, 8);
const headers = {
Accept: '*/*',
Cookie: `JSESSIONID="ajax:${sessionId}"`,
'Content-Type': 'application/x-www-form-urlencoded',
Referer: 'https://www.linkedin.cn/incareer/jobs/search',
'csrf-token': `ajax:${sessionId}`,
'x-http-method-override': 'GET',
'x-restli-protocol-version': '2.0.0',
};
return headers;
};
const period = {
1: 'r86400',
7: 'r604800',
30: 'r2592000',
};
const location = {
china: '102890883',
shanghai: '102772228',
beijing: '103873152',
};
const makeVariables = (variables) =>
'(' +
Object.entries(variables)
.filter(([, v]) => v)
.map(([k, v]) => `${k}:${encodeURIComponent(v)}`)
.join(',') +
')';
const makeBody = (body) => {
let output = '';
Object.entries(body).forEach(([key, value], index) => {
output += `${key}=${value}`;
if (index < Object.keys(body).length - 1) {
output += '&';
}
});
return output;
};
const ctxToTitle = (ctx) => {
const { keywords } = ctx.params;
const { geo, remote, location, period, relevant } = ctx.query;
const g = location || geo || 'China';
const r = remote ? '远程' : '';
const p = period ? `${period}` : '';
const o = relevant ? '相关' : '最新';
return `${o}${p}${g}${keywords || ''}${r}工作机会`;
};
const parseSearchHit = async (ctx) => {
const variables = {
origin: 'jserp',
isForRemoteJobsPage: !!ctx.query.remote,
isChinaMultiNationalCorporation: false,
count: ctx.query.limit || 20,
start: '0',
geoUrn: `urn:li:ks_geo:${location[ctx.query.location] || ctx.query.geo || location.china}`,
keywords: decodeURIComponent(ctx.params.keywords || ''),
f_TPR: period[ctx.query.period] || '',
sortType: ctx.query.relevant ? '' : 'DATE_DESCENDING',
};
const resp = await got.post(apiUrl, {
headers: makeHeader(),
body: makeBody({
operationName: 'searchSearchHitsByJob',
variables: makeVariables(variables),
queryId: searchHitQueryId,
}),
});
return {
jobs: resp.data.data.searchSearchHitsByJob.elements.map((e) => e.target.jobPosting),
title: ctxToTitle(ctx),
};
};
const parseJobPosting = async (jobPosting) => {
const entityUrn = jobPosting.entityUrn;
const variables = {
jobPostingUrn: entityUrn,
};
const resp = await got.post(apiUrl, {
headers: makeHeader(),
body: makeBody({
operationName: 'jobViewPage',
variables: makeVariables(variables),
queryId: jobPostingQueryId,
}),
});
const job = resp.data.data.jobsJobPostingsById;
return {
title: `${jobPosting.companyName} 正在找 ${jobPosting.title}`,
link: `https://www.linkedin.cn/incareer/jobs/view/${entityUrn.split(':').pop()}`,
guid: `linkedincn:${entityUrn}`,
description: art(path.join(__dirname, '../templates/cn/posting.art'), job),
pubDate: jobPosting.listedAt,
};
};
module.exports = {
parseSearchHit,
parseJobPosting,
};
+1
View File
@@ -1,3 +1,4 @@
module.exports = {
'/cn/jobs/:keywords?': ['bigfei'],
'/jobs': ['BrandNewLifeJackie26'],
};
+15 -1
View File
@@ -1,6 +1,6 @@
module.exports = {
'linkedin.com': {
_name: 'linkedin',
_name: 'LinkedIn',
'.': [
{
title: 'Job Listing',
@@ -19,4 +19,18 @@ module.exports = {
},
],
},
'linkedin.cn': {
_name: 'LinkedIn 领英中国',
'.': [
{
title: 'Jobs',
docs: 'https://docs.rsshub.app/other.html#linkedin-ling-ying-zhong-guo',
source: '/incareer/jobs/search',
target: (params, url) => {
const searchParams = new URL(url).searchParams;
return `/linkedin/cn/jobs/${searchParams.get('keywords') || ''}`;
},
},
],
},
};
+1
View File
@@ -1,3 +1,4 @@
module.exports = function (router) {
router.get('/cn/jobs/:keywords?', require('./cn/index.js'));
router.get('/jobs/:job_types/:exp_levels/:keywords?', require('./jobs.js'));
};
+25
View File
@@ -0,0 +1,25 @@
<h2>{{ title }}</h2>
<p>
{{ if (applyMethod.instantOffsiteApply)}}
<a href='{{applyMethod.instantOffsiteApply.companyApplyUrl}}'>点击申请</a>
{{ /if }}
</p>
<p>
{{ if (applyMethod.basicOffsiteApply)}}
<a href='{{applyMethod.basicOffsiteApply.companyApplyUrl}}'>点击申请</a>
{{ /if }}
</p>
<p>已有{{numApplies}}人申请此职位, {{numViews}}人查看此职位</p>
{{ if(compensationDescription) }}
<p>薪资{{ compensationDescription || 'N/A' }} </p>
{{ /if }}
<p>工作地点 {{ geo.defaultLocalizedName }} </p>
{{ if (company) }}
<h2>公司介绍</h2>
<p>{{company.name}}</p>
<p>员工人数{{company.employeeCount}}</p>
<p style="white-space: pre-line">{{company.localizedDescription}}</p>
{{ /if }}
<h2>职位介绍</h2>
<p style="white-space: pre-line">{{description.text}}</p>