Skip to content

Commit af6b69b

Browse files
committed
rewrite
1 parent 8a5b45e commit af6b69b

File tree

10 files changed

+261
-213
lines changed

10 files changed

+261
-213
lines changed

docker-compose.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ version: "3"
22
services:
33
wxgzh-api:
44
image: beautyyu/python-selenium:latest
5-
command: sh -c "pip3 install -r requirements.txt && python3 listen.py"
6-
restart: always # 自启动
7-
# ports:
8-
# - 11459:11459 # 默认监听端口
5+
command: sh -c "pip3 install -r requirements.txt && pip3 install waitress && python3 -m waitress server:app"
6+
restart: always
7+
ports:
8+
- 11459:8080
99
working_dir: /app
1010
volumes:
1111
- .:/app
1212
environment:
13-
DELAY: 3 #平均延迟时间(秒). 可能降低被微信后台风控的概率
14-
network_mode: host
13+
COOKIE_FILE: cookies.json

listen.py

Lines changed: 0 additions & 54 deletions
This file was deleted.

publish.py renamed to publish_deprecated.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,35 @@
66
from selenium.webdriver.support.ui import WebDriverWait
77
from selenium.webdriver.support import expected_conditions as EC
88
from selenium.webdriver.firefox.options import Options
9-
import sys, time, json, urllib, os
9+
import sys
10+
import time
11+
import json
12+
import urllib
13+
import os
1014
from xvfbwrapper import Xvfb
1115
delay = int(os.getenv('DELAY'))
16+
17+
1218
def get_by_css(driver, cssstr, multi=0, button=0):
1319
try:
1420
if button == 0:
15-
myElem = WebDriverWait(driver, 60).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, cssstr)))
21+
myElem = WebDriverWait(driver, 60).until(
22+
EC.presence_of_all_elements_located((By.CSS_SELECTOR, cssstr)))
1623
else:
17-
myElem = [WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.CSS_SELECTOR, cssstr)))]
24+
myElem = [WebDriverWait(driver, 60).until(
25+
EC.element_to_be_clickable((By.CSS_SELECTOR, cssstr)))]
1826
if multi == 1:
1927
return myElem
2028
else:
2129
return myElem[0]
2230
except:
2331
return 0
2432

33+
2534
def publish(appid=0):
2635
# load driver and cookies
27-
vdis = Xvfb()
28-
vdis.start()
36+
# vdis = Xvfb()
37+
# vdis.start()
2938
try:
3039
os.remove('geckodriver.log')
3140
except:
@@ -45,12 +54,15 @@ def publish(appid=0):
4554
token = urllib.parse.parse_qs(real_url)['token'][0]
4655
# check list and get appid
4756
driver.get('https://mp.weixin.qq.com/cgi-bin/appmsg?begin=0&count=10&type=77&action=list_card&token={}&lang=zh_CN'.format(token))
48-
appid = get_by_css(driver, 'div.publish_card_container:nth-child(2) > div:nth-child(1)').get_attribute('data-appid')
57+
appid = get_by_css(
58+
driver, 'div.publish_card_container:nth-child(2) > div:nth-child(1)').get_attribute('data-appid')
4959
# publish
5060
try:
5161
driver.get('https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit&action=edit&type=77&appmsgid={}&isMul=1&replaceScene=0&isSend=1&isFreePublish=0&token={}&lang=zh_CN'.format(appid, token))
52-
get_by_css(driver, '.mass-send__footer .weui-desktop-btn_primary', button=1).click()
53-
al = get_by_css(driver, '#vue_app > div:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > div:nth-child(1) > button', button=1)
62+
get_by_css(
63+
driver, '.mass-send__footer .weui-desktop-btn_primary', button=1).click()
64+
al = get_by_css(
65+
driver, '#vue_app > div:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > div:nth-child(1) > button', button=1)
5466
print(al)
5567
al.click()
5668
time.sleep(delay * 3)
@@ -59,8 +71,9 @@ def publish(appid=0):
5971
return -1
6072

6173
driver.close()
62-
vdis.stop()
74+
# vdis.stop()
6375
return 0
6476

77+
6578
if __name__ == "__main__":
6679
publish()

readme.md

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,93 @@
1-
# wxgzh-api - 获取微信公众号的最近文章
1+
# wxgzh-api - 获取任意微信公众号的最近文章
22

3-
本项目提供一种基于微信公众平台的方法获取微信公众号的最近文章
3+
本项目提供一种基于微信公众平台的方法,获取任意微信公众号的最近文章。
44

55
## 开始使用
66

7-
### 前置
7+
### Step 1. 准备
88

9-
你应当创建一个`微信公众平台订阅号`
9+
0. 创建一个`微信公众平台订阅号`
10+
1. 若使用`docker``docker-compose`部署, 则安装`docker``docker-compose`
1011

11-
你应当安装`docker``docker-compose`
12+
若手动部署,则安装`python3.10+`, `firefox`, `geckodriver`, 以及`requirements.txt`中的依赖
13+
2. 将项目克隆至本地
14+
```
15+
git clone https://github.com/BeautyYuYanli/wxgzh-api.git
16+
```
17+
3. 登录`微信公众平台`, 将`mp.weixin.qq.com`域名下的cookie以`json`格式保存至`wxgzh-api/cookies.json`. 你可以使用这个插件:[chrome](https://chrome.google.com/webstore/detail/%E3%82%AF%E3%83%83%E3%82%AD%E3%83%BCjson%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B-for-puppet/nmckokihipjgplolmcmjakknndddifde) [firefox](https://addons.mozilla.org/en-US/firefox/addon/%E3%82%AF%E3%83%83%E3%82%AD%E3%83%BCjson%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B-for-puppeteer/)
1218
13-
### 配置
19+
### Step 2. 部署
1420
15-
1. 将本项目克隆至本地
16-
```
17-
git clone https://github.com/BeautyYuYanli/wxgzh-api.git
18-
```
21+
#### 方法一、 docker + docker-compose
1922
20-
2. 登录`微信公众平台`, 将`mp.weixin.qq.com`域名下的cookie以`json`格式保存至`wxgzh-api/cookies.json`. 你可以使用这个插件:[chrome](https://chrome.google.com/webstore/detail/%E3%82%AF%E3%83%83%E3%82%AD%E3%83%BCjson%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B-for-puppet/nmckokihipjgplolmcmjakknndddifde) [firefox](https://addons.mozilla.org/en-US/firefox/addon/%E3%82%AF%E3%83%83%E3%82%AD%E3%83%BCjson%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B-for-puppeteer/)
23+
1. `docker-compose up -d`
2124
22-
3. 其它配置在`docker-compose.yml`自行修改
25+
#### 方法二、 手动部署
2326
24-
### 部署
27+
1. `pip3 install waitress`
28+
2. `python3 -m waitress --port=11459 server:app`
2529
26-
完成配置后在`wxgzh-api`目录下执行
27-
```
28-
docker-compose up -d
29-
```
30-
以启动服务
30+
### Step 3. 使用
3131
32-
### 使用
32+
已部署的服务默认在 `localhost:11459` 监听。
3333
34-
服务默认在`localhost:11459`监听. 你可以发送一个`post`请求, 并携带`utf-8`编码的参数`公众号1$公众号2$...$公众号n`, 如:
34+
`/json_feed`: `GET`, 多个参数 `target`, 返回 [RSS JSON Feed](https://www.jsonfeed.org/2020/08/07/json-feed-version.html) (`application/feed+json`)格式的数据:
3535
```
36-
requests.post("http://127.0.0.1:11459", "大连理工大学$大连理工大学学生会".encode('utf-8')).text
36+
http://127.0.0.1:11459/json_feeds?target=声动活泼&target=汪小喵爱大工
3737
```
38-
如果服务正常运行, 将返回`json`格式的字符串:
3938
```json
4039
{
41-
"大连理工大学":[
42-
{"date": "2021-01-16", "author": "大连理工大学", "title": "文章1", "link": "http://mp.weixin.qq.com/s?..."},
43-
{"date": "2021-01-15", "author": "大连理工大学", "title": "文章2", "link": "http://mp.weixin.qq.com/s?..."},
44-
],
45-
"大连理工大学学生会":[
46-
{"date": "2021-01-15", "author": "大连理工大学学生会", "title": "文章3", "link": "http://mp.weixin.qq.com/s?..."},
47-
{"date": "2021-01-14", "author": "大连理工大学学生会", "title": "文章4", "link": "http://mp.weixin.qq.com/s?..."},
48-
]
40+
"title": "微信公众号",
41+
"version": "https://jsonfeed.org/version/1.1",
42+
"description": "微信公众号文章更新推送",
43+
"home_page_url": "https://github.com/BeautyyuYanli/wxgzh-api",
44+
"items": [
45+
{
46+
"authors": [
47+
{
48+
"name": "声动活泼"
49+
}
50+
],
51+
"date_published": "2023-03-05T00:00:00+08:00",
52+
"title": "一年了!声动胡同有了这些新变化,邀请你来加入",
53+
"url": "http://mp.weixin.qq.com/s?__biz=MzIwMDczNTE3OQ==&mid=2247496071&idx=1&sn=7024a904a4cf6f448ebbcc2888cf282c&chksm=96fa1123a18d983599aba221d7d1a8cbd0c80d2773f9cdb1b13c2ead9cadcfe905f8ad94fe4f#rd"
54+
},
55+
{
56+
"authors": [
57+
{
58+
"name": "汪小喵爱大工"
59+
}
60+
],
61+
"date_published": "2023-03-05T00:00:00+08:00",
62+
"title": "公告&记录 | 查询汪小喵精神状态",
63+
"url": "http://mp.weixin.qq.com/s?__biz=MzI4NzYwMTYxMQ==&mid=2247487382&idx=1&sn=7774bfc2e7fed6473982b191fac1c225&chksm=ebca6932dcbde024ed77f52f855fe61beb6fcfb49f3700482a52d7c6e0185ddab043968ab052#rd"
64+
}
65+
]
4966
}
50-
```
51-
否则返回错误信息
5267
53-
### Warning
68+
```
5469

55-
**为了避免被微信后台风控, 查询过程将长达`1min`以上**
70+
也可以在不部署的情形下直接拉取数据:
71+
```
72+
python standalone.py -h
73+
```
74+
```
75+
usage: standalone.py [-h] [--cookiefile COOKIEFILE] [--target TARGET [TARGET ...]]
5676
57-
**出于同样的理由, 请谨慎地控制调用该API的频率**
77+
options:
78+
-h, --help show this help message and exit
79+
--cookiefile COOKIEFILE
80+
--target TARGET [TARGET ...]
5881
59-
### Demo
82+
```
6083

61-
本项目**可能触发微信后台的风险管控**, 故不提供demo. 你可以查看姊妹项目[wxgzh2tg](https://github.com/BeautyYuYanli/wxgzh2tg.git)的demo:
84+
## 开发
6285

63-
- Telegram Channel: [DUT_News](https://t.me/s/DUT_News)
86+
本项目还可以直接作为 Python 模块使用(TODO)
6487

65-
-[RSSHub](https://github.com/DIYgod/RSSHub)协作, 将文章进一步转为[rss订阅链接](https://rsshub.app/telegram/channel/DUT_News)
88+
## 其他
6689

67-
## Todo
90+
- 频繁请求可能导致账号被风控。
91+
- Cookies 有效期为 3 天,过期后需要重新获取。
6892

69-
- [ ] 增加查询失败处理
93+
本项目还可能对账号造成其它未知的影响, 请自行承担风险。

requirements.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
requests
2-
selenium
3-
xvfbwrapper
1+
requests>=2.28.1
2+
selenium>=4.7.2
3+
python-dateutil>=2.8.2
4+
flask>=2.2.0

server.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import os
2+
from flask import Flask, request, make_response
3+
from wxgzh_api.updater import Updater
4+
from wxgzh_api.updater.exceptions import CookieException
5+
6+
app = Flask(__name__)
7+
8+
9+
@app.route('/json_feeds', methods=['GET'])
10+
def json_feeds():
11+
try:
12+
updater = Updater(cookiefile=os.getenv('COOKIE_FILE'))
13+
except CookieException as e:
14+
return make_response(str(e), 500)
15+
except Exception as e:
16+
return make_response(str(e), 500)
17+
result = updater.update(request.args.getlist('target'))
18+
content = {
19+
"version": "https://jsonfeed.org/version/1.1",
20+
"title": "微信公众号",
21+
"home_page_url": "https://github.com/BeautyyuYanli/wxgzh-api",
22+
"description": "微信公众号文章更新推送",
23+
"items": [item for sublist in result.values() for item in sublist]
24+
}
25+
return make_response(content, 200, {'Content-Type': 'application/feed+json'})

standalone.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
import argparse
3+
from wxgzh_api.updater import Updater
4+
from wxgzh_api.updater.exceptions import CookieException
5+
6+
if __name__ == "__main__":
7+
argparser = argparse.ArgumentParser()
8+
argparser.add_argument('--cookiefile', type=str,
9+
default=os.getenv('COOKIE_FILE'))
10+
argparser.add_argument('--target', type=str, nargs='+', default=[])
11+
args = argparser.parse_args()
12+
updater = Updater(cookiefile=args.cookiefile)
13+
result = updater.update(args.target)
14+
print(result)

0 commit comments

Comments
 (0)