爬虫
无论是做科研还是做开发者都需要一个数据基础,有时是图片、音频,又有时是一张表格、一句评论。但凡是所有能在互联网上触及到的东西,爬虫都能够把它们采集下来,整理成你需要的数据。这里就不过多介绍了,更多介绍请看维基。
抓包工具
抓包是写爬虫过程中必不可少的部分,目的是获取请求过程的请求URL
、Headers
、请求体
等信息,然后编写程序模拟这个过程,好的爬虫总会是模拟一个真实的人类行为,来进行请求数据。抓包工具我只推荐Reqable:全平台,特别推荐,我也在用。其他的可以去查查,工具只要顺手就行,达到目的就可以。
Python网络请求库
requests | aiohttp | httpx | pycurl | curl_cffi | |
---|---|---|---|---|---|
http2 | ❌ | ❌ | ✅ | ✅ | ✅ |
sync | ✅ | ❌ | ✅ | ✅ | ✅ |
async | ❌ | ✅ | ✅ | ❌ | ✅ |
websocket | ❌ | ✅ | ❌ | ❌ | ✅ |
fingerprints | ❌ | ❌ | ❌ | ❌ | ✅ |
speed | 🐇 | 🐇🐇 | 🐇 | 🐇🐇 | 🐇🐇 |
Python常用的网络请求库如上所示(来源于curl_cffi),其中最常用的是requests,可能是api比较方便吧。
从上表可以看出,支持异步的有httpx和curl_cffi,pycurl不推荐。httpx被称作为是下一代python网络请求库,我也非常推荐日常爬虫使用这个库,因为用它写异步特别方便。
一个httpx异步程序可以是这样的:
import asyncio
import httpx
import time
start_time = time.time()
async def get_pokemon(client, url):
resp = await client.get(url)
pokemon = resp.json()
return pokemon['name']
async def main():
async with httpx.AsyncClient() as client:
tasks = []
for number in range(1, 151):
url = f'https://pokeapi.co/api/v2/pokemon/{number}'
tasks.append(asyncio.ensure_future(get_pokemon(client, url)))
original_pokemon = await asyncio.gather(*tasks)
for pokemon in original_pokemon:
print(pokemon)
asyncio.run(main())
print("--- %s seconds ---" % (time.time() - start_time))
NOTE需要注意的是,异步请求之所以快是因为网络阻塞,如果网络阻塞很小的话(例如进行本地请求)使用异步反而会减速。这样的话使用多线程会更快
curl_cffi是一个可以模拟浏览器指纹的请求库,官方宣称速度比httpx还快,我还没有尝试,毕竟还没遇到与浏览器指纹拦截相关的问题,速度的话httpx已经非常快了并且curl_cffi的普及度好像很小,感兴趣的可以去了解了解
还有一种最万能的爬虫方案:使用selenium或者playwright这两个web自动化框架,如果都没接触过推荐学习playwright。下面是一个大神的国内社交媒体的爬虫,使用playwright实现,非常强大!
Python网络解析库
网络解析有几种方法,主要是解析HTML,因为json格式很好读取,解析也很方便。
NOTEjson解析的话,除了python内置的json库,还可以用性能更高的ujson库,之前读取一个2GB多的json文件用内置库很卡,ujson很快
而解析HTML格式的话,需要进行标签和属性匹配,得到具体想要的数据,例如图片链接、表格数据等。常用的解析方式有正则(re)、BeautifulSoup(bs4)、Xpath(lxml) 三种方式,其中BeautifulSoup和Xpath适合简单地解析,正则用来提取出特定规则的数据。以下提供这三个库的简单的api调用
# re https://docs.python.org/zh-cn/3.12/library/re.html
import re
line = "Cats are smarter than dogs"
matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)
if matchObj:
print "matchObj.group() : ", matchObj.group()
print "matchObj.group(1) : ", matchObj.group(1)
print "matchObj.group(2) : ", matchObj.group(2)
else:
print "No match!!"
# BeautifulSoup pip install bs4
from bs4 import BeautifulSoup
# 读取示例 HTML 文件
html = """
<html>
<head>
<title>Example Page</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
"""
# 创建 BeautifulSoup 对象,并指定解析器为 lxml
soup = BeautifulSoup(html, 'lxml')
# 找到第一个 h1 标签,并输出其文本内容
h1 = soup.find('h1')
print(h1.text)
# Xpath pip install lxml
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0" id="id1"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0" id="id2"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
print(etree.tostring(html))
数据保存
简单的数据保存推荐使用pandas.DataFrame
将需要的字段以二维表的方式存储起来,然后选择输出文件类型(csv, xlsx, pickle)
因为爬取过程中会出现反爬等原因导致请求不到数据,然后就会需要耗费时间将之前的请求再请求一遍或者调整代码之类的,如果是单文件的话就无法将之前的数据保存为文件,无法持久化数据,使用jupyter notebook可以解决此问题但我觉得还不是最佳方案,我认为可以使用sqlite数据库然后用ORM库 SqlModel来映射,这样就可以持久化数据而且使用强大的SQL来查询、筛选数据。
框架
python爬虫框架最常用的是Scrapy
,使用框架的好处是只需要考虑数据解析的问题,其他网络请求、数据保存、错误处理等问题框架都提供了比较完善的方案,再次不多做说明,毕竟用到框架是处理很复杂的爬虫方案的,我目前还没接触到,学习框架也会浪费一定精力。