Featured image of post Ruia笔记

Ruia笔记

零碎的岛屿,终会找到海,慢慢亦漫漫

快速开始

基于Ruia快速实现一个以Hacker News为目标的爬虫 本文主要通过对hacker News的爬取示例来展示如何使用ruia。

第一步:定义item

item的目的是定义目标网站你需要爬取的数据,此时,爬虫的目标数据就是页面中的title和url,怎么提取数据,ruia的field类提供了以下三种方式提取目标数据

  • xpath
  • re
  • css selector

本教程爬虫例子都默认使用css selector的规则来提取目标数据

规则确定后,就可以用 item来实现一个针对目标数据的 orm,创建文件 items.py

1
2
3
4
5
6
from ruia import AttrField,TextField,Item

class HackerNewsItem(Item):
    target_item = TextField(css_select='tr.athing')
    title = TextField(css_select='a.storylink')
    url = AttrField(css_select='a.storylink',attr='href')

这段代码含义是:针对我们提取的目标html,我们定义了一个HackerNewsItem类,其包含了两个field

  • title:直接从文本提取
  • url:从属性提取

target_item是什么。对于一个 Item类来说,当其定义好网页目标数据后,ruia提供两种方式进行获取 Item

  • get_item:获取网页的单目标,比如目标网页的标题,此时无需定义target_item
  • get_items:获取网页的多目标,比如当前目标网页Hacker News中的title和url一共有30个,这时就必须定义target_item来寻找多个目标块;target_item的作用就是针对这样的工作而诞生的,开发者只要定义好这个属性(此时Ruia会自动获取网页中30个target_item),然后每个target_item里面包含的title和url就会被提取出来

第二步:测试Item

Ruia为了方便扩展以及自由地组合使用,本身各个模块之间耦合度是极低的,每个模块都可以在你的项目中单独使用;你甚至只使用ruia.Item、Ruia.TextField和ruia.AttrField来编写一个简单的爬虫

第三步:编写Spider

Ruia.Spider是Ruia框架里面的核心控制类

  • 控制目标网页的请求 Ruia.RequestRuia.Response
  • 可加载自定义钩子,插件以及相关配置等,让开发效率更高

开发者实现 HackerNewsSpider 必须是 Spider的子类,代码出现的两个方法 Spider内置:

  • parse:此方法是Spider的入口,每一个start_urls的响应必然会被parse方法捕捉并执行;
  • process_item:此方法作用是抽离出对Item提取结果的处理过程,比如这里会接受自定义Item类作为输入,然后进行处理持久化到文件。

第四步:运行Start

如果你想在异步函数里面调用,执行**await HackerNewsSpider.start()**即可

第五步:扩展

Middleware的目的是对每次请求前后进行一番处理 Ruia已经专门编写了一个名为 ruia-ua的插件来为开发者提升效率。示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from ruia import AttrField, TextField, Item, Spider
from ruia_ua import middleware


class HackerNewsItem(Item):
    target_item = TextField(css_select='tr.athing')
    title = TextField(css_select='a.storylink')
    url = AttrField(css_select='a.storylink', attr='href')


class HackerNewsSpider(Spider):
    start_urls = ['https://news.ycombinator.com/news?p=1', 'https://news.ycombinator.com/news?p=2']

    async def parse(self, response):
        # Do something...
        print(response.url)


if __name__ == '__main__':
    HackerNewsSpider.start(middleware=middleware)

MongoDB

数据持久化,如果想将数据持久化到数据库(MongoDB)中,该怎么做?此时就到了凸显Ruia插件优势的时候了,你只需要安装 ruia-motor

 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
from ruia_motor import RuiaMotorInsert, init_spider

from ruia import AttrField, Item, Spider, TextField


class HackerNewsItem(Item):
    target_item = TextField(css_select="tr.athing")
    title = TextField(css_select="a.storylink")
    url = AttrField(css_select="a.storylink", attr="href")


class HackerNewsSpider(Spider):
    start_urls = [f"https://news.ycombinator.com/news?p={index}" for index in range(3)]
    concurrency = 3
    # aiohttp_kwargs = {"proxy": "http://0.0.0.0:1087"}

    async def parse(self, response):
        async for item in HackerNewsItem.get_items(html=await response.text()):
            yield RuiaMotorInsert(collection="news", data=item.results)


async def init_plugins_after_start(spider_ins):
    spider_ins.mongodb_config = {"host": "127.0.0.1", "port": 27017, "db": "ruia_motor"}
    init_spider(spider_ins=spider_ins)


if __name__ == "__main__":
    HackerNewsSpider.start(after_start=init_plugins_after_start)

入门指南

概览

Ruia是一个基于 asyncioaiohttp的异步爬虫框架,理念:

  • 更少的代码:能通用的功能就插件化,让开发者直接引用即可
  • 更快的速度:由异步驱动

安装

定义Item

运行 Spider

基础概念

Request

Request的主要作用是方便处理网络请求,最终返回一个 Request对象

主要提供的方法有

  • Request().fetch:请求一个网页资源,可以单独使用
  • Request().fetch_callback:为 Spider类提供的核心方法

核心参数

  • url:请求的资源连接
  • method:请求的方法
  • callback:回调函数
  • headers:请求头
  • load_js:目标网页是否需要加载js
  • metadata:跨请求传递的一些数据
  • request_config:请求配置
  • request_session: aiohttp的请求 session
  • aiohttp-kwargs:请求目标资源可定义的其他参数

Request通过对 aiohttppyppeteer的封装来实现对网页资源的异步请求

Response

Respoonse的目的是返回一个统一切友好的响应对象

  • url:请求的资源连接
  • metadata:跨请求传递的一些数据
  • html:源网站返回的资源数据
  • cookies:网站cookie
  • history:访问历史
  • headers:请求头
  • status:请求状态码

Item

Item的主要作用是定义以及通过一定的规则提取网页中的目标数据,它主要提供了

  • get_item:针对页面单目标数据进行提取
  • get_items:针对页面多目标数据进行提取

Core arguments

  • html:网页源码
  • url:网页连接
  • html_etree:etree._Element对象

不论是源网站链接或者网站HTML源码,甚至是经过lxml处理过的etree._Element对象,Item能接收这三种类型的输入并进行处理

有时你会遇见这样一种情况,例如爬取Github的Issue时,你会发现一个Issue可能对应多个Tag。 这时,将Tag作为一个独立的Item来提取是不划算的, 我们可以使用Field字段的many=True参数,使这个字段返回一个列表。

最终 Item类会将输入最终转化为etree._Element对象进行处理,然后利用元类的思想将每一个 Field构造的属性计算为原网页上对应的真实数据

Selector

Selector通过Field类实现,为开发者提供了CSS SelectorXPath两种方式提取目标数据,具体由下面两个类实现:

  • AttrField(BaseField):提取网页标签的属性数据
  • TextField(BaseField):提取网页标签的text属性

核心参数

所有 Field共有的参数

  • default:str;设置默认值,建议定义,否则找不到字段时会报错
  • many:bool,返回值将是一个列表

AttrField TextField HtmlField共有参数

  • css_select:str,利用 CSS Select提取目标数据
  • xpath_select:str,利用 XPath提取目标数据

AttrField需要一个额外的参数

  • attr:目标标签属性

RegexField需要一个额外的参数

  • re_select:str,正则表达式字符串

定好CSS SelectorXPath规则,然后利用lxml实现对目标html进行目标数据的提取

Spider

Spider是爬虫程序的入口,它将Item,Middleware,Request,等模块组合在一起,从而为你构造一个稳健的爬虫程序。你只需要关注以下两个函数:

  • Spider.start:爬虫的启动函数
  • parse:爬虫的第一层解析函数,继承 Spider的子类必须实现这个函数

核心参数:

Spider.start的参数

  • after_start:爬虫启动后的钩子函数
  • before_stop:爬虫启动前的钩子函数
  • middleware:中间件类,可以是一个中间件 **Middleware()**实例,也可以是一组 **Middleware()**实例组成的列表
  • loop:事件循环

Spider会自动读取start_urls列表里面的请求链接,然后维护一个异步队列,使用生产消费者模式进行爬取,爬虫程序一直循环直到没有调用函数为止

Middleware

Middleware的主要作用是在进行一个请求的前后进行一些处理,比如监听请求或者响应:

  • Middleware().request:在请求前处理一些事情
  • Middleware().response:在请求后处理一些事情

使用中间件有两点需要注意,一个是处理函数需要带上特定的参数,第二个是不需要返回值,具体使用如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from ruia import Middleware

middleware = Middleware()

@middleware.request
async def print_on_request(spider_ins, request):
    """
    每次请求前都会调用此函数
    request: Request类的实例对象
    """
    print("request: print when a request is received")
    
@middleware.response
async def print_on_response(spider_ins, request, response):
    """
    每次请求后都会调用此函数
    request: Request类的实例对象
    response: Response类的实例对象
    """
    print("response: print when a response is received")

Middleware通过装饰器来实现对函数的回调,从而让开发者可以优雅的实现中间件功能,Middleware类中的两个属性request_middlewareresponse_middleware分别维护着一个队列来处理开发者定义的处理函数

最后更新于 Oct 19, 2024 22:55 CST
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计