AirSpider

AirSpider是一款轻量爬虫,学习成本低。面对一些数据量较少,无需断点续爬,无需分布式采集的需求,可采用此爬虫。

1. 创建爬虫

命令参考:命令行工具

示例

  1. feapder create -s air_spider_test
  2. 请选择爬虫模板
  3. > AirSpider
  4. Spider
  5. TaskSpider
  6. BatchSpider

生成如下

  1. import feapder
  2. class AirSpiderTest(feapder.AirSpider):
  3. def start_requests(self):
  4. yield feapder.Request("https://www.baidu.com")
  5. def parse(self, request, response):
  6. print(response)
  7. if __name__ == "__main__":
  8. AirSpiderTest().start()

可直接运行

2. 代码讲解

默认生成的代码继承了feapder.AirSpider,包含 start_requestsparser 两个函数,含义如下:

  1. feapder.AirSpider:轻量爬虫基类
  2. start_requests:初始任务下发入口
  3. feapder.Request:基于requests库类似,表示一个请求,支持requests所有参数,同时也可携带些自定义的参数,详情可参考Request
  4. parser:数据解析函数
  5. response:请求响应的返回体,支持xpath、re、css等解析方式,详情可参考Response

除了start_requests、parser两个函数。系统还内置了下载中间件等函数,具体支持可参考BaseParser

3. 自定义解析函数

开发过程中解析函数往往不止有一个,除了系统默认的parser外,还支持自定义解析函数,写法如下

  1. def start_requests(self):
  2. yield feapder.Request("https://www.baidu.com", callback=self.parser_xxx)
  3. def parser_xxx(self, request, response):
  4. print(response)

即feapder.Request支持指定callback函数,不指定时默认回调parser

4. 携带参数

有时我们需要把前面请求到的数据携带到下一级,写法如下

  1. def start_requests(self):
  2. yield feapder.Request("https://www.baidu.com", xxx="我是携带的数据")
  3. def parse(self, request, response):
  4. xxx = request.xxx
  5. print(xxx)

直接在feapder.Request中携带即可,xxx为携带数据的key,可以随意写,只要不和feapder.Request默认参数冲突即可。默认参数参考Request。可以携带任意类型的值,如字典、类等

取值:如何携带就如何取值,如上我们携带xxx, 那么request.xxx 可将xxx值取出,取出的值和携带的值类型一致。

5. 下发新任务

parser中支持下发新任务,写法与start_requests一致,只需要yield feapder.Request即可。示例如下:

  1. def parse(self, request, response):
  2. yield feapder.Request("url1") # 不指定callback,任务会调度默认的parser上
  3. yield feapder.Request("url2", callback=self.parser_detail) # 指定了callback,任务由callback指定的函数解析

6. 下载中间件

下载中间件用于在请求之前,对请求做一些处理,如添加cookie、header等。写法如下:

  1. def download_midware(self, request):
  2. request.headers = {'User-Agent':"lalala"}
  3. return request

request.参数, 这里的参数支持requests所有参数,同时也可携带些自定义的参数,详情可参考Request

默认所有的解析函数在请求之前都会经过此下载中间件

7. 自定义下载中间件

与自定义解析函数类似,下载中间件也支持自定义,只需要在feapder.Request参数里指定个download_midware回调即可,写法如下:

  1. def start_requests(self):
  2. yield feapder.Request("https://www.baidu.com", download_midware=self.xxx)
  3. def xxx(self, request):
  4. """
  5. 我是自定义的下载中间件
  6. :param request:
  7. :return:
  8. """
  9. request.headers = {'User-Agent':"lalala"}
  10. return request

自定义的下载中间件只有指定的请求才会经过。其他未指定下载中间件的请求,还是会经过默认的下载中间件

8. 校验

  1. def validate(self, request, response):
  2. """
  3. @summary: 校验函数, 可用于校验response是否正确
  4. 若函数内抛出异常,则重试请求
  5. 若返回True 或 None,则进入解析函数
  6. 若返回False,则抛弃当前请求
  7. 可通过request.callback_name 区分不同的回调函数,编写不同的校验逻辑
  8. ---------
  9. @param request:
  10. @param response:
  11. ---------
  12. @result: True / None / False
  13. """
  14. pass

例如:

  1. def validate(self, request, response):
  2. if response.status_code != 200:
  3. raise Exception("response code not 200") # 重试
  4. # if "哈哈" not in response.text:
  5. # return False # 抛弃当前请求

9. 失败重试

框架支持重试机制,下载失败或解析函数抛出异常会自动重试请求。

例如下面代码,校验了返回的code是否为200,非200抛出异常,触发重试

  1. def parse(self, request, response):
  2. if response.status_code != 200:
  3. raise Exception("非法页面")

默认最大重试次数为100次,我们可以引入配置文件或自定义配置来修改重试次数,详情参考配置文件

10. 爬虫配置

爬虫配置支持自定义配置或引入配置文件setting.py的方式。

配置文件:在工作区间的根目录下引入setting.py,具体参考配置文件

-w261

自定义配置: 使用类变量__custom_setting__

  1. class AirSpiderTest(feapder.AirSpider):
  2. __custom_setting__ = dict(
  3. PROXY_EXTRACT_API="代理提取地址",
  4. )

上例是配置代理提取地址,以便爬虫使用代理,自定义配置支持配置文件中的所有参数。

配置优先级: 自定义配置 > 配置文件,即自定义配置会覆盖配置文件里的配置信息,不过自定义配置只对自己有效,配置文件可以是多个爬虫公用的

AirSpider不支持去重,因此配置文件中的去重配置无效

11. 加快采集速度

默认爬虫为1线程,我们可通过修改线程数来加快采集速度。除了在配置文件中修改或使用自定义配置外,可以在启动函数中传递线程数

  1. if __name__ == "__main__":
  2. AirSpiderTest(thread_count=10).start()

12. 数据入库

框架内封装了MysqlDBRedisDB,与pymysql不同的是,MysqlDB 使用了线程池,且对方法进行了封装,使用起来更方便。RedisDB 支持 哨兵模式、集群模式。使用方法如下:

导入

  1. from feapder.db.mysqldb import MysqlDB
  2. from feapder.db.redisdb import RedisDB

以mysql为例,获取mysql对象

方式1:若setting.py__custom_setting__中指定了数据库连接信息,则可以直接以db = MysqlDB()方式获取

  1. class AirSpiderTest(feapder.AirSpider):
  2. __custom_setting__ = dict(
  3. MYSQL_IP="localhost",
  4. MYSQL_PORT = 3306,
  5. MYSQL_DB = "feapder",
  6. MYSQL_USER_NAME = "feapder",
  7. MYSQL_USER_PASS = "feapder123"
  8. )
  9. def __init__(self, *args, **kwargs):
  10. super().__init__(*args, **kwargs)
  11. self.db = MysqlDB()

方式2:若没配置文件,则MysqlDB需要传递连接信息

  1. db = MysqlDB(
  2. ip="localhost",
  3. port=3306,
  4. user_name="feapder",
  5. user_pass="feapder123",
  6. db="feapder"
  7. )

MysqlDB 的具体使用方法见 MysqlDB

RedisDB 的具体使用方法见 RedisDB

框架也支持数据自动入库,详见数据自动入库

13. 浏览器渲染下载

采集动态页面时(Ajax渲染的页面),常用的有两种方案。一种是找接口拼参数,这种方式比较复杂但效率高,需要一定的爬虫功底;另外一种是采用浏览器渲染的方式,直接获取源码,简单方便

使用方式:

  1. def start_requests(self):
  2. yield feapder.Request("https://news.qq.com/", render=True)

在返回的Request中传递render=True即可

框架支持CHROMEPHANTOMJS两种浏览器渲染,可通过配置文件进行配置。相关配置如下:

  1. # 浏览器渲染
  2. WEBDRIVER = dict(
  3. pool_size=1, # 浏览器的数量
  4. load_images=True, # 是否加载图片
  5. user_agent=None, # 字符串 或 无参函数,返回值为user_agent
  6. proxy=None, # xxx.xxx.xxx.xxx:xxxx 或 无参函数,返回值为代理地址
  7. headless=False, # 是否为无头浏览器
  8. driver_type="CHROME", # CHROME 或 PHANTOMJS,
  9. timeout=30, # 请求超时时间
  10. window_size=(1024, 800), # 窗口大小
  11. executable_path=None, # 浏览器路径,默认为默认路径
  12. render_time=0, # 渲染时长,即打开网页等待指定时间后再获取源码
  13. )

14. 自定义下载器

自定义下载器即在下载中间件里下载,然后返回response即可,如使用httpx库下载以便支持http2

  1. import feapder
  2. import httpx
  3. class AirSpeedTest(feapder.AirSpider):
  4. def start_requests(self):
  5. yield feapder.Request("http://www.baidu.com")
  6. def download_midware(self, request):
  7. with httpx.Client(http2=True) as client:
  8. response = client.get(request.url)
  9. return request, response
  10. def parse(self, request, response):
  11. print(response)
  12. if __name__ == "__main__":
  13. AirSpeedTest(thread_count=1).start()

注意,解析函数里的response已经变成了下载中间件返回的response,而非默认的。若想用xpath、css等解析功能,写法如下

  1. import feapder
  2. import httpx
  3. from feapder.network.selector import Selector
  4. class AirSpeedTest(feapder.AirSpider):
  5. def start_requests(self):
  6. yield feapder.Request("http://www.baidu.com")
  7. def download_midware(self, request):
  8. with httpx.Client(http2=True) as client:
  9. response = client.get(request.url)
  10. return request, response
  11. def parse(self, request, response):
  12. selector = Selector(response.text)
  13. title = selector.xpath("//title/text()").extract_first()
  14. print(title)

15. 主动停止爬虫

  1. import feapder
  2. class AirTest(feapder.AirSpider):
  3. def start_requests(self):
  4. yield feapder.Request("http://www.baidu.com")
  5. def parse(self, request, response):
  6. self.stop_spider() # 停止爬虫,可以在任意地方调用该方法
  7. if __name__ == "__main__":
  8. AirTest().start()

16. 完整的代码示例

AirSpider:https://github.com/Boris-code/feapder/blob/master/tests/air-spider/test_air_spider.py

浏览器渲染:https://github.com/Boris-code/feapder/blob/master/tests/test_rander.py