最近北京2022冬奥会备受关注,不管是开幕式,还是运动员的比赛。看了几场比赛,感觉这些冰上运动根本不属于我的阶级。那就看看排行榜吧,那么,哪里的数据最权威呢?当然是北京2022冬奥会官网 :https://www.beijing2022.cn/en/ 。看了看实现方式,发现这个 Olympic Medal Table 的数据似乎是考虑了高并发,它并未采用api的方式获取,而是直接和页面一起返回的。看了一下响应头,如下图所示
果然选用了高并发服务器。先不管这么多,直接上 requets 吧,试了一下发现返回内容里面没有包含排行榜的table,估计又是页面的js在搞鬼,我也懒得看js代码了(其实是没找到),使用大招:requests-html 直接拿下。安装方式:
pip install requests-html
那么,现在已经有奖牌榜了,该提供api出去了,记得之前我写过一些使用python抓取数据然后使用php向外部提供api接口的,现在想一想,那样写限制太多了,不能施展出Python大法的威力。所以这次准备挑选一个Python的api框架,那就 FastAPI 吧。安装方式参考官网如下:
pip install fastapi[all]
根据官网的快速开始随便写了一下,接下了就是把上面写的爬虫和这个fastapi结合一下就ok了,可是事情的发展总是出乎意料。
当我第一次尝试结合的时候,发现requests-html 调用了chromium 且使用了 async/awiat ,但是fastapi的快速开始代码里面没有 async 的方法,我也不清楚能不能加,最后在官方文档中找到了:https://fastapi.tiangolo.com/zh/async/ 可算是解决了问题,那么windows测试运行是没有问题了。
当我把代码传到linux上运行后,又出现了问题,报错如下:
pyppeteer.errors.BrowserError: Browser closed unexpectedly
???最终通过 https://blog.csdn.net/u011054333/article/details/81055423 提供的解决方法成功解决,即对于 Pyppeteer 来说,python:3.7 内置的依赖库并不够,我们还需要额外进行安装。
apt-get update && \
apt-get -y install libnss3 xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
最终花费一个晚上加一个上午,终于可以运行了。
看一下官网的页面,对比一下:
目结构如下:
+pyapi
- beijing2022.py
- main.py
这样可以很方便扩展。
beijing2022.py
# beijing2022 冬奥会奖牌榜爬虫
# api.sencom.top:7000/beijing2022
# Author: BH6AOL
# Date: 2022-02-13
from bs4 import BeautifulSoup
from requests_html import AsyncHTMLSession
class OMT:
"""
Olympic Medal Table
"""
def __init__(self, order, noc, gold, silver, bronze, total, order_by_total):
self.order = order
self.noc = noc
self.gold = gold
self.silver = silver
self.bronze = bronze
self.total = total
self.order_by_total = order_by_total
URL = "https://results.beijing2022.cn/beijing-2022/olympic-games/en/results/all-sports/medal-standings.htm"
cookies = {
'acw_tc': '701ea19f16447610114827772e4bac216b906bcf729ef26bd42cf3d69e',
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate, br',
'Referer': 'https://results.beijing2022.cn/beijing-2022/olympic-games/en/results/all-sports/medalists.htm',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'If-Modified-Since': 'Sun, 13 Feb 2022 12:24:10 GMT',
'Cache-Control': 'max-age=0',
'TE': 'trailers',
}
async def medal_standings():
session = AsyncHTMLSession()
response = await session.get(URL, headers=headers, cookies=cookies)
await response.html.arender()
soup = BeautifulSoup(response.html.html, 'html.parser')
session.browser.close() # 及时关闭浏览器,否则内存溢出有你好果汁吃!
omt_list = []
for tr in soup.tbody.find_all("tr"):
tds = tr.find_all("td")
order = tds[0].text.strip()
noc = tds[1].text.strip()
gold = tds[2].text.strip()
silver = tds[3].text.strip()
bronze = tds[4].text.strip()
total = tds[5].text.strip()
order_by_total = tds[6].text.strip()
omt = OMT(order, noc, gold, silver, bronze, total, order_by_total)
omt_list.append(omt)
return omt_list
main.py
# api.sencom.top:8888
# 各种由Python 编写的接口合集
# Author: BH6AOL
# Date: 2022-02-13
import beijing2022
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def index():
return "欢迎来到 BH6AOL 的个人 API 站点"
@app.get("/beijing2022")
async def get_beijing2022():
try:
medal_standings = await beijing2022.medal_standings()
return {"status": "1", "data": medal_standings}
except Exception as e:
return {"status": "0", "data": e}
依赖:
- python3
- fastapi
- bs4
- requests-html
后台运行命令:
root@VM-16-3-debian:~/app/pyapi# uvicorn main:app --port 8888 > a.log 2>&1 &
可以使用nginx 将其代理出去,比如这样配置nginx.conf
server {
listen 7000;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:8888/;
}
}
这样就可以通过外网访问了