「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
最近查询一下河北有哪些小吃,找来找去不是很方便,发现百度有个接口,顺手牵一下数据,通过各省份的枚举,把数据都整理到 Excel 中,以后在查询就比较方便了。 下图为最终抓取数据格式,都是好吃的。
爬取前的分析
在百度搜索省市的名字+小吃,翻阅页面会找到如下内容,每页展示 12 条小吃数据。
随机点击一个页码,通过开发者工具,可以捕获到 API 请求的接口,拿到 API 之后获取数据就变得特别简单了,API 接口地址与参数如下:
https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?resource_id=28589&from_mid=1&&format=json&ie=utf-8&oe=utf-8&query=%E5%8C%97%E4%BA%AC%E5%B0%8F%E5%90%83&sort_key=&sort_type=1&stat0=&stat1=&stat2=&stat3=&pn=12&rn=12&cb=jQuery110200006770667994311363_1605510465709&_=1605510465720
URL 参数非常多,简单整理之后,核心的参数如下:
https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?resource_id=28589&query=%E9%A6%99%E6%B8%AF%E5%B0%8F%E5%90%83&pn=1&rn=100
参数说明:
- resource_id=28589 资源类型 固定值
- query 查询内容,经测试为 省市名字 + 小吃 二字即可
- pn=1&rn=100 pn 起始条数,rn 每页条数,经测试这两个值可以直接设置为 1 和 100(可以在放大)就可以获取到完整的数据了,不用再按照分页获取展示。
获取到的 JSON 数据格式为:
重要的数据在红框部分,通过 API 获取红框部分数据即可。
在正式编码之前,还需要准备一份中国各省份的名称字典或者元组。
PROVINCE = (
(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, '西藏自治区')
)
PROVINCE_DICT = dict(PROVINCE)
分析完毕,就可以进入实际的编码环节了。
小吃数据抓取编码实现
首先按照上述内容分析出来的 URL 格式,用字符串模板结合省份名称批量生成待爬取的 URL 列表。
def get_allurl():
url_format = "https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?resource_id=28589&query={province}小吃&pn=1&rn=100"
urls_tuple = [(province, url_format.format(province=province))
for province in PROVINCE_DICT.values()]
return urls_tuple
拿到该页面可以用单线程去抓取,也可以用多线程直接抓取都可以,由于数据量不大就 34 个省份的数据,简单些直接用单线程爬取即可。
本次依旧将数据存储到 csv 文件中,提前创建一个带列名的 csv 文件。
if __name__ == "__main__":
with open('xiaochi.csv', 'w', newline='') as f:
fieldnames = {"additional", 'ename', 'img',
'query', "imgScale", "pic_4n_78", "selpic", "province"}
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
urls_tuple = get_allurl()
get_data(urls_tuple)
核心代码部分也非常简单,循环上述构造成的待爬取 URL 即可,单线程整体无难度,依旧是对 requests 的一个基础应用。
def get_data(urls_tuple):
# 循环爬取各省份小吃
for url in urls_tuple:
print(f"正在爬取{url[1]}页面小吃数据")
r = requests.get(url=url[1], headers=headers)
time.sleep(2)
# 得到JSON数据
data = r.json()
if data["status"] == 0:
print("数据获取成功")
# 获取result数据
result = data["data"][0]["result"]
if len(result) > 0:
print("小吃数据正常")
# 循环保存数据
for item in result:
item["province"] = url[0]
with open('xiaochi.csv', 'a+', newline='') as f:
fieldnames = {"additional", 'ename', 'img',
'query', "imgScale", "pic_4n_78", "selpic", "province"}
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writerow(item)
else:
print("无小吃数据")
代码运行之后,控制一下抓取频率,数据会依次保存在本地。
最终抓取到了 2260 条数据,数据量虽说不大,但是可以做很多有意思的事情了。
最后在啰嗦两句
前两天看到一个朋友留言说,好爬的数据不值钱,值钱的数据不好爬。 很有道理,可是咱是学习阶段,又不是写商业爬虫,再说商业爬虫也是依赖这些简单的技术进行操作的,无非爬取的门槛高了一些,需要你把简单的知识点迭代在一起操作。
爬取与反爬取永远是一个斗智斗勇的过程,爬虫工程师一般也可以做好反爬工作。
学习完爬虫小课 9 讲,希望你对 requests 库有一个比较全面的认识。后面 3 讲将补充 post 自动化操作,requests 代理等内容。