Python很适合做一些数据采集和自动化任务。
最近我比较常用的一套组合就是:
requests
+
BeautifulSoup前者负责请求数据,后者负责解析HTML。
requests:先把网页拿回来
最简单的请求示例:
import requests
response = requests.get(
"https://example.com"
)
print(response.status_code)
print(response.text)其中:
response.status_code可以看HTTP状态码。
而:
response.text可以拿到响应正文。
别忘了超时
一个很容易被忽略的问题:
requests.get(url)没设超时。
如果目标服务器一直不响应,程序可能卡很久。
实际代码里我习惯:
response = requests.get(
url,
timeout=10
)这样至少不会无限等下去。
HTTP Headers
有些网站不会接受太简单的请求。
可以加:
headers = {
"User-Agent": "Mozilla/5.0"
}
response = requests.get(
url,
headers=headers,
timeout=10
)如果接口要身份认证,还可以加对应的:
Authorization
Cookie
Referer等请求头。
Session
如果需要连续访问多个页面,我比较推荐用Session。
session = requests.Session() # 创建Session
session.get(login_url) # 登录
session.get(user_url) # 使用登录后的Session去继续请求,就不用每次请求前都得登录一次(主要是频繁登录还可能导致账号进入风控)Session可以在多个请求之间保存Cookie等状态。
对于需要登录的网站尤其方便。
BeautifulSoup
拿到HTML之后:
from bs4 import BeautifulSoup
soup = BeautifulSoup(
response.text,
"html.parser"
)然后就可以找元素了:
title = soup.find("title")取文本:
print(title.get_text())或者:
links = soup.find_all("a")遍历:
for link in links:
print(link.get("href"))CSS Selector
如果页面结构比较复杂,可以用CSS Selector:
items = soup.select(".item")或者:
titles = soup.select("article h2")这比不断嵌套find()更容易读。
但别看到网页就解析HTML
这是我后来做数据采集时比较明显的习惯变化。
很多网站虽然浏览器里看到的是HTML,但真正的数据可能来自API。
比如:
浏览器
↓
HTML
↓
JavaScript
↓
API
↓
JSON这种情况下,与其解析最终页面,不如直接去找数据API。
如果API返回:
{
"items": [
{
"id": 1,
"name": "Test"
}
]
}直接处理JSON通常比解析HTML稳定很多。
数据采集最重要的是稳定性
能跑起来的爬虫不难写。
真正麻烦的是:
网络失败
请求超时
页面结构变化
登录状态失效
接口限流
数据为空
编码问题所以实际项目里,我一般至少会考虑:
timeout
异常处理
重试
日志
缓存
请求频率而不是只写一句:
requests.get(url)别给服务器制造压力
数据采集时也要考虑请求频率。
比如:
for url in urls:
requests.get(url)如果URL很多,短时间内大量请求可能会给目标服务器造成压力。
更合理的做法是:
控制请求频率
合理设置并发
缓存已经拿到的数据
只拿需要的数据同时也该遵守目标网站的使用规则。
requests + BeautifulSoup的定位
我觉得这套工具最大的优点就是简单。
requests
↓
拿数据
BeautifulSoup
↓
解析HTML如果任务只是:
- 抓取页面
- 提取标题
- 提取链接
- 读取公开信息
- 做简单的数据整理
它已经够好用了。
如果目标网站是高度动态的SPA,或者数据全是JavaScript渲染的,那就应该去分析网络请求,而不是一味靠HTML解析。
总结
Python的优势之一就是有大量很实用的小工具。
requests和BeautifulSoup就属于这种。
它们本身不复杂,但凑到一起之后,能快速搞定很多自动化工作。
对我来说,做数据采集时最重要的也逐渐从"怎么把数据抓下来",变成了:
怎么稳定、合理、可维护地把数据抓下来。