知识中心

我们的骄傲!非遗数据采集,来自官方的数据,Python爬虫无所不爬

本次要抓取的数据为《中国非物质文化遗产数字博物馆》,仅技术学习。
有一说一,非遗数据网站做的非常漂亮。

目标数据源分析

目标站点:http://www.ihchina.cn/,数据存在下图所示位置:

原则上可以针对所有分类抓取,为降低网站访问频率,只采集单一分类,即 http://www.ihchina.cn/project#target1

页面数据为异步加载,通过点击分页获得如下数据:

http://www.ihchina.cn/Article/Index/getProject.html?province=&rx_time=&type=&cate=&keywords=&category_id=16&limit=10&p=1
http://www.ihchina.cn/Article/Index/getProject.html?province=&rx_time=&type=&cate=&keywords=&category_id=16&limit=10&p=2

其中参数分别为:

  • province:所属地区;
  • rx_time:公布时间;
  • type:类别;
  • cate:类型;
  • keywords:关键词;
  • category_id:分类 ID;
  • limit:每页数据量;
  • p:页码。

本案例整体代码应该与上一案例相似,重点要看的是本次的数据返回。下图为服务器响应数据,其中核心数据存在于 list 当中,但开发人员将分页格式进行了返回,分页 HTML 标签进行了返回,以上数据在部分项目中存在参考价值。

编码时间

本次为 threading 模块的最后一个案例,在初级爬虫阶段,掌握基本的多线程应用即可。

在实测过程中,有一个经典的采集技巧可以使用,即测试服务器单次接口最大返回数据量,在本案例中,你可以手动修改上述请求地址的 limit 参数,例如将其修改为 100,该值会返回 100 条数据。

出现上述情况,就说服务器对单次返回数据没有限制,所以原则上你是可以直接修改为 3610(目标分类所有数据)的。

这样就可以实现一次访问接口,获取全部数据(不过当返回的数据量大时,接口相应速度会变慢,建议根据实际情况调整) 。

完整代码如下所示

import threading
import requests
import random 
class Common:    def __init__(self):        pass
    def get_headers(self):        uas = [            "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)",            "你自己的UA值,或者可以从之前的博客中获取"        ]        ua = random.choice(uas)        headers = {            "user-agent": ua,            "referer": "https://www.baidu.com"        }        return headers
def run(index, url, semaphore, headers):    semaphore.acquire()  # 加锁    res = requests.get(url, headers=headers, timeout=5)    res.encoding = 'utf-8'    text = res.text    save(index,text)    semaphore.release()  # 释放

# 存储的数据中文进行了 UNICODE 编码,分析的时候注意转化
def save(index, text):    with open(f"./非遗数据/{index}.json", "w", encoding="utf-8") as f:        f.write(f"{text}")    print("该URL地址数据写入完毕")
if __name__ == '__main__':    c = Common()    url_format = 'http://www.ihchina.cn/Article/Index/getProject.html?province=&rx_time=&type=&cate=&keywords=&category_id=16&limit=10&p={}'    # 拼接URL,全局共享变量,362 页直接设置,没有动态获取    urls = [url_format.format(i) for i in range(1, 362)]    # 最多允许5个线程同时运行    semaphore = threading.BoundedSemaphore(5)    for i, url in enumerate(urls):        t = threading.Thread(target=run, args=(i, url, semaphore, c.get_headers()))        t.start()    while threading.active_count() != 1:        pass    else:        print('所有线程运行完毕')
/resources/upload/a18e3a3febaa5b1/1630567097367/style.css /resources/upload/a18e3a3febaa5b1/1630566937973/jquery.min.js /resources/upload/a18e3a3febaa5b1/1630567091482/script.js