Linkedin API

最近有在看 LinkedIn 的 API 文档,在这里记录一下:

用户文章 API

获取用户发布的文章

LinkedIn API 曾经是支持经过用户授权后获取发布的文章的,相关的 OAuth scope 是 r_member_social ,但是 LinkedIn 移除了相关的 scope 和 API,就再也无法获取用户的文章了。

r_member_social is a closed permission

发布文章

需要的 scope w_member_social

公司组织 API

获取用户管理的公司组织

处理返回结果的时候要注意下 "state": "APPROVED" 才算是用户有权限管理的公司

获取公司组织详情

注意 URL 当中的公司组织 ID 是数字,比如 79988552

获取公司组织文章

需要的 scope r_organization_social

Sample Request

1
2
3
4
5
## Get Organization Posts By Organization
curl "https://api.linkedin.com/rest/posts?author=urn%3Ali%3Aorganization%3A104928924&q=author&count=10&sortBy=LAST_MODIFIED" \
-H 'X-Restli-Protocol-Version: 2.0.0' \
-H 'Linkedin-Version: 202408' \
-H 'Authorization: Bearer Token'

OAuth 权限说明

上面那些 API 主要用到下面几个权限

1
2

openid email profile w_member_social r_organization_social w_organization_social r_organization_admin
Permission Description
openid
email
profile
w_member_social 发布用户文章
r_organization_social 获取公司组织的文章
w_organization_social 发布公司组织的文章
r_organization_admin 获取用户管理的公司组织

其他

新版本 API

LinkedIn 在 2022 年的时候将 API 地址从 https://api.linkedin.com/v2/ 变更成了 https://api.linkedin.com/rest/ , 详情如下:

token 缓存 bug

LinkedIn 的 OAuth token 有个 5 分钟的缓存问题,即使你更换了新的 token,LinkedIn 后端仍然使用旧的 token,新 token 必须等待 5 分钟才能生效。例如,你新 token 添加了一些新的权限,但你请求相关权限 API 时仍然会提示权限不足,过了 5 分钟后就可以成功调用了。

相关链接

LinkedIn API postman

官方文档

Developer Apps

更新历史

  • 2024-09-06 首次更新

LinkedIn Twitter爬虫研究

TLDR

Twitter 和 LinkedIn 都需要登陆账号才能抓取到内容,不管你使用无头浏览器还是非公开的 API.

Detail

背景

项目需要抓取用户在 Twitter 和 LinkedIn 上发布的内容,但官方 API 无法满足需求。Twitter API 似乎只能获取最近 7 天的推文,而 LinkedIn 根本没有提供获取用户内容的 API。因此,需要研究其他抓取方式。

Twitter

尝试过用无头浏览器在不登录的情况下抓取推文,发现 Twitter 对不同的账号有不同的处理方式:

  • 有些账号在不登录的情况下可以获取到最新的推文,比如 nasa
  • 一些账号只能显示老的推文
  • 剩余部分的账号不登录无法查看推文

用无头浏览器的问题是你需要去解析 dom 来提取推文,比较麻烦。后来发现 GitHub 上有个名叫 trevorhobenshield/twitter-api-client 的库使用了未公开的 API 抓取推文:

APIFY 上有个类似的 actor 感觉也是用了这个库:

使用这个库需要登陆 Twitter 账号,有抓取频率限制,如果超出了限额会返回 429 状态码,如果在 serverless 那种经常变更 ip 的环境使用,会触发 Twitter 的安全策略,需要你手动登录账号输入验证码来解锁账号。

还有一个名叫 social tools 的第三方网站提供 API 来抓取推文,没有用过所以这里不做评价。

Twitter 对创建账号也有限制,需要邮箱认证和验证码,感觉验证码的问题可以用 AI 来解决。

说白了,就是得刷号才能抓到大量推文。

LinkedIn

LinkedIn 官方曾经提供过 API 来获取用户发布的文章,但是后来把这个接口给删除了,估计是担心被人抓取后用来训练 AI。

LinkedIn 也有那种使用非公开 api 的库,名叫 tomquirk/linkedin-api

同样也需要登录账号,LinkedIn 的安全策略比 Twitter 严格的多,手机号码验证,图形验证码。如果你的账号被锁定,LinkedIn 还需要你上传身份证才能解锁账号。

LinkedIn 官方提供 API,可在用户授权后获取用户管理的公司发布的文章

Github Copilot 使用体验

使用 GitHub Copilot 已经一年多了,来分享一下我的使用体验:

首先说优点:

代码优化助手

有时你觉得一段代码写得有点啰嗦,但没有查阅相关文档,你并不知道是否有更好的写法。这时 Copilot 就能提供一些更简洁的写法。

例如,我之前写 GitHub Actions 时遇到过这样一个问题:下面的代码是部分 Actions 的代码,它的功能是 SSH 到 VPS 上执行一些固定的脚本。由于我有多台 VPS,每多一台 VPS 都要添加一段类似的代码,唯一的区别是 VPS 的端口号、IP 地址等变量。

原始代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- name: Deploy to US
uses: appleboy/ssh-action@v1.0.3
env:
REALITY_SERVER_NAME: "www.xxx.com"
with:
host: "1.1.1.1:2222"
username: "root"
key: ${{ secrets.SSH_VPS }}
envs: REALITY_SERVER_NAME
script: |
scripts...

- name: Deploy to JP
if: ${{ success() }}
uses: appleboy/ssh-action@v1.0.3
env:
REALITY_SERVER_NAME: "www.xxx.com"
with:
host: "2.2.2.2:444"
username: "root"
key: ${{ secrets.SSH_VPS }}
envs: REALITY_SERVER_NAME
script: |
scripts...

我中途研究了一下 appleboy/ssh-action 的用法,想看看它是否支持多台服务器,但最终发现它不符合我的需求。我还查了一下 GitHub Actions 目前是否支持 YAML 锚点。最后我问了 Copilot:“Is this github action code a bit redundant, is there room for optimization?”

Copilot 随后给出了修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
runs-on: ubuntu-latest
strategy:
matrix:
server:
- {
name: "server1",
host: "1.1.1.1:2222",
username: "root",
reality_server_name: "www.xxx.com",
}
- {
name: "server2",
host: "2.2.2.2:4444",
username: "root",
reality_server_name: "www.xxx.com",
}

steps:
- name: Deploy to ${{ matrix.server.name }}
uses: appleboy/ssh-action@v1.0.3
env:
REALITY_SERVER_NAME: ${{ matrix.server.reality_server_name }}
with:
host: ${{ matrix.server.host }}
username: ${{ matrix.server.username }}
key: ${{ secrets.SSH_VPS }}
envs: REALITY_SERVER_NAME
script: |
scripts...

如果没有 copilot 或者你没有看过一遍 github actions 的所有文档的话,你是不知道还有 matrix 这种用法的。

Demo 代码生成

有时候想用不熟悉的语言写个 Demo 程序,或者对框架和库不太熟悉的时候,Copilot 可以帮你快速写出示例代码。

冗余代码生成

copilot 特别适合生成一些 verbose code,特别是在写增删改查代码的时候,比如写一些 controller,生成一些 entity 代码。

接下来说一下缺点:

生成的代码可能包含一些不明显的 bug

我认为这是 Copilot 最大的缺点:它有时无法完全理解你的意图,生成的代码会包含一些不易察觉的 bug。比如以下代码(Kotlin,已简化):

1
2
3
4
5
6
val a = 1;
val b = 2;

function add(x:Int, y:Int): Int {
return x + y;
}

你本来意图是些下面这段代码:

1
val c = add (a, b)

但实际上 copilot 生成的这段代码

1
val c = add (a, a)

生成的代码会通过编译,如果你不去 review 一下你是很难发现这个 bug 的。

又或者下面这段代码:

1
2
3
4
5
val map = mutablMapOf<String,String>();

map.put("id" , a)
map.put("name" , b) -- 本来你想写的代码
map.put("username", b) -- copilot 生成的代码

本来要写的是 name 但是 copilot 生成的是 username ,这种 bug 也比较难发现。

数据库更新不及时

有时你想让 Copilot 生成某个框架的代码示例,但它只能给出旧版本的代码示例,因为 Copilot 的数据还没有包含最新版本的框架数据。

幻觉

这属于 LLM 的功能特性,而非 bug。例如,它生成的代码可能会调用一些不存在的方法,或者列出一些不存在的库。

插件支持

感觉 VS Code 上的 Copilot 比 JetBrains 上的好用,总觉得 JetBrains 上的 Copilot 有点笨。

总的来说,Copilot 有明显的优缺点。在使用 Copilot 生成的代码时,一定要仔细检查。如果使用得当,感觉可以提高不少效率。

infuse刮削逻辑研究

我最近研究了 Infuse 的刮削逻辑,发现它的官方文档写得不太清楚。我用 Infuse + 阿里云盘的方式测试了一下,总结如下:

剧集

1
2
3
folder1/foder2/file1.mkv
folder1/foder2/file2.mkv

  • 对于剧集来说,folder1folder2 的命名并不重要,Infuse 并不关心它们的名称。关键在于 file1 的文件名。如果你希望 Infuse 将 folder2 识别为一个剧集,那么 file1file2 的文件名必须遵循 剧集名称.第几季.第几集.后缀名 的格式,例如 Peppa.Pig.S01.E01.mkv 表示《小猪佩奇》第一季第一集。
  • 官方文档显示剧集信息源自 TMDB,但实际测试下来似乎并非如此。如有剧集名称疑问,可手动修改 file1 元数据,并参考搜索结果中的剧集名称进行命名。
  • 如需批量重命名文件,可以使用 Mountain Duck + Alist 挂载阿里云盘,然后利用 macOS 的批量重命名功能。Mountain Duck + 阿里云盘官方 WebDAV 无法重命名文件,会返回 403 错误。

排除文件夹

文档中的方法测试了下没有效果,不知到是啥原因,只能把文件夹移动到别的地方。

I/O操作中阻塞线程的具体表现

在 I/O 操作中阻塞线程的具体表现:

  1. 系统调用层面:
    • 当程序执行 I/O 操作(如读取文件)时,它会发起一个系统调用。
    • 在传统的阻塞 I/O 模型中,这个系统调用会导致线程进入等待状态。
  2. 线程状态变化:
    • 线程从”运行”状态转变为”阻塞”状态。
    • 操作系统会将这个线程从 CPU 的执行队列中移除。
  3. CPU 资源释放:
    • 阻塞的线程不再占用 CPU 时间片。
    • CPU 可以切换到其他就绪的线程执行。
  4. 等待 I/O 完成:
    • 线程保持阻塞状态,直到 I/O 操作完成。
    • 完成可能需要相对较长的时间(相对于 CPU 速度而言)。
  5. 唤醒过程:
    • I/O 操作完成后(通常通过中断机制通知 CPU),操作系统将线程标记为就绪状态。
    • 线程重新进入就绪队列,等待被调度执行。
  6. 继续执行:
    • 当线程再次获得 CPU 时间片时,它从 I/O 操作的下一条指令继续执行。

阻塞的影响:

  • 资源利用率低:阻塞的线程无法执行其他任务,即使 I/O 操作耗时很长。
  • 并发处理能力受限:每个阻塞的 I/O 操作都可能需要一个专门的线程,限制了系统能同时处理的 I/O 操作数量。

java NIO

  • 非阻塞 I/O:
    • NIO 允许线程发起 I/O 操作后立即返回,而不是等待操作完成。
    • 线程可以继续执行其他任务,而不是被阻塞。
  • 多路复用:
    • NIO 使用选择器(Selector)来管理多个通道(Channel)。
    • 一个线程可以监控多个 I/O 操作的状态。
  • 工作原理:
    • 线程不会”阻塞然后去做别的事”,而是根本不会阻塞。
    • 它会持续检查多个 I/O 操作的状态,只在数据就绪时才进行实际的 I/O 操作。
  • 实际操作流程:
    a. 设置通道为非阻塞模式。
    b. 将多个通道注册到一个选择器上。
    c. 线程调用选择器的 select()方法。
    d. 选择器返回已经就绪的通道。
    e. 线程对就绪的通道执行 I/O 操作。

A context switch can occur while the kernel is executing a system call on behalf of the user. If the system call blocks because it is waiting for some event to occur, then the kernel can put the current process to sleep and switch to another process. For example, if a system call requires a disk access, the kernel can opt to perform a context switch and run another process instead of waiting for the data to arrive from the disk. Another example is the system call, which is an explicit request to put the calling process to sleep. In general, even if a system call does not block, the kernel can decide to perform a context switch rather than return control to the calling process.

Dropbox Operation not permitted 问题修复

Dropbox 在 macOS Ventura 中将同步目录从 ~/Dropbox 移至 /Users/xxx/Library/CloudStorage/Dropbox 后,使用 iTerm ls 命令访问该目录会提示 Operation not permitted 错误。这是因为 iTerm 对新目录没有访问权限。

解决方法:打开 system settingsPrivacy&SecurityFull Disk AccessiTerm,为 iTerm 启用访问权限即可。

这里还有一个问题就是如果你将 ~/.ssh 目录符号链接到同步文件夹会有权限问题,同步文件夹的上级目录的权限是不一样的。

2024-03-16 水

我还活着 。。。

最近把家里的黑苹果升级成了 Sonoma,苹果在 Sonoma 版本移除了博通网卡驱动,这导致无线没法用了,突然有点想换回 Windows 了。

如何在装有windows的macbook pro下开启虚拟化

家里有一台常年吃灰的 2015 款 MacBook Pro,最近想体验下 WSL2,就打算废物利用,将这台 Mac 格式化安装了 Windows,安装完成后发现未开启虚拟化,由于我只安装了 windows 系统,没法通过 boot camp 的方式来开启虚拟化,研究了下可以通过安装 rEFInd 来开启虚拟化:

修改配置:

  1. 打开 refind文件夹,找到 refind.conf-sample文件,重命名为 refind.conf
  2. 用文本编辑器打开它,搜索 enable_and_lock_vmx,把它的值改为 true,并且注意把前面的 # 号去掉

我已经两年没有更新博客了,因为我觉得之前写的文章缺乏实质性内容,再加上工作比较忙,所以暂停了两年的更新。最近有些闲暇时间,所以升级了一下仓库:

  • 升级了 hexo 版本,换成了默认的主题,并修改了部署方式。新的部署方式只需要一个仓库就可以了。
  • 集成了 Notion,现在在 Notion 上发布的文章可以手动同步到站点上。此功能使用了以下插件:Doradx/notion2markdown-action

参考:

Bitbucket Pipeline问题记录

Bitbucket Pipelines configuration reference

无法在 stage中使用 parallel

比如下面会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- stage: &deploy
name: Deploy
deployment: default
parallel:
steps:
- step:
name: Deploy store backend
oidc: true
caches:
- node
script:
- npm i
- export CDK_DEPLOY_REGION=$AWS_REGION
- export ENVIRONMENT=$BITBUCKET_DEPLOYMENT_ENVIRONMENT
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- npx cdk deploy test-$BITBUCKET_DEPLOYMENT_ENVIRONMENT-store-stack --require-approval never
- step:
name: Deploy store step function
oidc: true
caches:
- node
script:
- npm i
- export CDK_DEPLOY_REGION=$AWS_REGION
- export ENVIRONMENT=$BITBUCKET_DEPLOYMENT_ENVIRONMENT
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- npx cdk deploy test-$BITBUCKET_DEPLOYMENT_ENVIRONMENT-store-sfn-solana-stack --require-approval never

关键点在于 deployment 属性只能属于 step 或者 stage ,但是parallel 会有多个step ,但是 deployment 属性只能出现一次

Limitations

- The maximum number of steps you can within a stage is:
    - 10 steps for workspaces on the [Free plan](https://www.atlassian.com/software/bitbucket/pricing).
    - 100 steps for workspaces on a [Standard or Premium plan](https://www.atlassian.com/software/bitbucket/pricing).
- Parallel stages are not supported.
- A stage can't include parallel steps.
- A stage can't contain manually triggered steps, but you can configure a stage to be manually triggered.
- A stage can't contain conditional steps, but you can configure a stage to be conditional.
- Disabling artifact downloads inside a stage is not supported.
- Steps can't override any property also set on a stage.
- Partially completed deployment stages can't be continued if another change was subsequently deployed to the same environment.

传递文件变量

Bitbucket pipeline 可以通过Repository variables来传递变量,但是如果变量包含一些特殊字符比如换行符,bitbucket 就不能很好的处理,对于这种情况我们可以将变量用 base64 编码一下,在 pipeline 中再解码就可以解决这问题了。

1
2
3
4
cat file.txt | base64

// in pipeline file
echo ${YOUR_ENV} | base64 -d > file.txt

Condition 用法

Documentation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- step: &deployStepFunction
name: Deploy store step function
oidc: true
caches:
- node
condition:
changesets:
includePaths:
- "lib/sfn.ts"
- "sfn/**"
- "bin/**"
script:
- npm i
- export CDK_DEPLOY_REGION=$AWS_REGION
- export ENVIRONMENT=$BITBUCKET_DEPLOYMENT_ENVIRONMENT
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- npx cdk deploy test-$BITBUCKET_DEPLOYMENT_ENVIRONMENT-store-sfn-solana-stack --require-approval never

限制

  • A step within the stage can’t contain a condition. The condition must be defined on the stage.

例如,下面这段就会引发上述错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
image: node:18

pipelines:
definitions:
- stage: &deploy
name: Deploy to Default
deployment: default
steps:
- step:
name: Install
caches:
- node
script:
- npm install
- step:
oidc: true
name: Deploy Admin Stack
condition:
changesets:
includePaths:
- "lib/admin.ts"
script:
- export CDK_DEPLOY_REGION=$AWS_REGION
- export ENVIRONMENT=$BITBUCKET_DEPLOYMENT_ENVIRONMENT
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- npx cdk deploy "project-${ENVIRONMENT}-admin-stack" --require-approval never
- step:
oidc: true
name: Deploy Backend Stack
condition:
changesets:
includePaths:
- "lib/backend.ts"
script:
- export CDK_DEPLOY_REGION=$AWS_REGION
- export ENVIRONMENT=$BITBUCKET_DEPLOYMENT_ENVIRONMENT
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- npx cdk deploy "project-${ENVIRONMENT}-backend-stack" --require-approval never

branches:
master:
- stage:
<<: *deploy
name: Deploy to demo
deployment: demo
local:
- stage:
<<: *deploy
name: Deploy to charles
deployment: charles

Parallel

内存问题

pipstep 共享内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
default:
- step:
services:
- redis
- mysql
- docker
script:
- echo "This step is only allowed to consume 2048 MB of memory"
- echo "Services are consuming the rest. docker 512 MB, redis 512 MB, mysql 1024 MB"
definitions:
services:
redis:
image: redis:3.2
memory: 512
docker:
memory: 512 # reduce memory for docker-in-docker from 1GB to 512MB
mysql:
image: mysql:5.7
# memory: 1024 # default value
variables:
MYSQL_DATABASE: my-db
MYSQL_ROOT_PASSWORD: $password

OIDC Connect

Artifact

不支持环境变量名 , 比如:

1
2
3
4
5
6
7
8
9
10
script:
- apt-get update && apt-get install -y zip
- python3 -m pip install --upgrade pip
- pip install poetry
- poetry export --only $FUNCTION -f requirements.txt -o ./$FUNCTION/requirements.txt --without-hashes --without-urls
- cd $FUNCTION
- zip -r $FUNCTION.zip .

artifacts:
- $FUNCTION/$FUNCTION.zip

通配符用法

1
2
artifacts:
- '*.zip'

Multiple Lines Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pipelines:
definitions:
- step: &deploy
oidc: true
caches:
- node
name: CDK Deploy
script:
- npm install
- export AWS_ROLE_ARN=$AWS_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- >
if [ -z "$STACK_NAME" ]; then
echo "STACK_NAME is not set";
npx cdk deploy --all --require-approval never
else
echo "STACK_NAME is set to '$STACK_NAME'";
npx cdk deploy $STACK_NAME --require-approval never
fi
1
2
3
4
5
6
- >
functions=("1" "2" "3")
//这里要换行 不然报错
for function in "${functions[@]}"; do
gcloud functions deploy $(echo $function | tr '_' '-') --source=gs://$BUCKET_NAME/$function.zip --region=us-west2
done

echo 报错问题

比如下面包含 :空格 会报错:

1
- echo "Groups to package: ${groups[@]}"

改成

1
- echo "Groups to package:\ ${groups[@]}"