小吃搜搜乐,弄点小吃数据放在本地、Python 爬虫小课 6-9

400 阅读4分钟

「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

最近查询一下河北有哪些小吃,找来找去不是很方便,发现百度有个接口,顺手牵一下数据,通过各省份的枚举,把数据都整理到 Excel 中,以后在查询就比较方便了。 下图为最终抓取数据格式,都是好吃的。 小吃搜搜乐,弄点小吃数据放在本地、Python 爬虫小课 6-9

爬取前的分析

在百度搜索省市的名字+小吃,翻阅页面会找到如下内容,每页展示 12 条小吃数据。

小吃搜搜乐,弄点小吃数据放在本地、Python 爬虫小课 6-9

随机点击一个页码,通过开发者工具,可以捕获到 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 数据格式为:

小吃搜搜乐,弄点小吃数据放在本地、Python 爬虫小课 6-9

重要的数据在红框部分,通过 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("无小吃数据")

代码运行之后,控制一下抓取频率,数据会依次保存在本地。

小吃搜搜乐,弄点小吃数据放在本地、Python 爬虫小课 6-9

最终抓取到了 2260 条数据,数据量虽说不大,但是可以做很多有意思的事情了。

在这里插入图片描述

最后在啰嗦两句

前两天看到一个朋友留言说,好爬的数据不值钱,值钱的数据不好爬。 很有道理,可是咱是学习阶段,又不是写商业爬虫,再说商业爬虫也是依赖这些简单的技术进行操作的,无非爬取的门槛高了一些,需要你把简单的知识点迭代在一起操作。

爬取与反爬取永远是一个斗智斗勇的过程,爬虫工程师一般也可以做好反爬工作。

学习完爬虫小课 9 讲,希望你对 requests 库有一个比较全面的认识。后面 3 讲将补充 post 自动化操作,requests 代理等内容。