Skip to main content
Join the official 2019 Python Developers SurveyStart the survey!

a spider using asyncio

Project description

aspider

一个简单好用的Python 异步爬虫

简介

aspider 是一个基于Python asyncio 开发的爬虫工具, 可以通过命令行使用, 不用写任何代码, 也可以调用api, 自定义具体爬取的规则.
aspider的设计原则是专注于爬取网页, 本身不包含如解析文本, 数据存储等内容, 这些都可以利用很多已有的工具框架自行加上. aspider 参考了
类似flask的路由装饰器的方法, 只需要定义一些方法, 每个方法用于爬取一个相应的正则路径.

author: gxtrobot

系统设计原理

  • 系统使用常用的生产者, 消费者模式, 生产者产生需要爬取的链接并加入队列, 消费者从队列获取链接进行爬取, 并解析其内容和链接, 调用相关的方法, 和用户自定义的方法, 符合要求的链接会加到队列, 这样只通过一个根路径就可以扩展到全站爬取
  • 系统才用asyncio的并发协程, 每个并发协程同时担任消费者和生产者的指责, 通过一个queue(队列)进行交互
  • 系统定义了一个路由(Router), 作为用户唯一需要使用的对象, 路由不需要新建, 系统只有一个Router对象, 直接获取就可以使用
  • 用户所有自定义方法, 都通过路由提供的装饰器来实现, 例如route, 无需创建class或其他复杂的逻辑

安装

To install use pip:

$ pip install aspider

Or clone the repo:

$ git clone https://github.com/gxtrobot/aspider.git
$ python setup.py install

开始使用

命令行方式

aspider 安装完成后可以直接通过命令 aspider 使用, 或 python -m aspider

 aspider --help
usage: aspider [-h] [--max_redirect N] [--max_tries N] [--max_tasks N]
               [-p PROXY] [-i INCLUDE_REGEX] [-e EXCLUDE_REGEX] [-o OUTPUT]
               [-c COUNT] [--nostrict] [--no_parse_links] [-v] [-q]
               [roots [roots ...]]

An Asyncio Web crawler, version: 0.1.0

positional arguments:
  roots                 要爬取的网页路径(一般为跟路径), 可以有多个

optional arguments:
  -h, --help            显示帮助信息
  --max_tasks N         并发任务数, 默认为5个, 如需要可以提高
  -p PROXY, --proxy PROXY
                        代理使用, 例如 http://localhost:1080
  -i INCLUDE_REGEX, --include INCLUDE_REGEX
                        需要匹配爬取的网页路径正则, 匹配的网页链接将被加入爬取队列
  -o OUTPUT, --output OUTPUT
                        输出的方法, 目前支持这几种[txt, json, stream], stream为命令行输出
  -c COUNT, --count COUNT
                        限制爬取的数量, 如指定, 到限额后爬虫自动退出, 队列未完成的会取消
  --no_parse_links      禁止解析链接, 指定后, 只爬取命令行给定的roots链接, 不会跟踪解
                        页面包含的链接
  -v, --verbose         debug 消息数据, 可以有多个v (-v , -vv)
  -q, --quiet           静默输出, 只打印错误消息

命令行使用例子

  • 豆瓣250电影抓取

运行以下命令, 将会爬取top250的10页html, 并存到json文件中

aspider https://movie.douban.com/top250 --include /top250.* --out json

不过这通常并没太大用处, 因为这样只是抓取完整的html, 只适合有时候需要抓取整个网页, 然后用其他工具处理, 或者不懂python 开发, 可以先用这个抓取网页, 然后用其他工具处理

保存的json文件是json line格式, 就是说每行是一个json对象, 读取时候要相应处理, 每读取一行, 将该行内容转为json对象, python 代码如下

def read_json_lines():
    file = 'top250.json'
    with open(file) as f:
        for line in f:
            d = json.loads(line)
            print(d.keys()

API 介绍

routing 模块 - Router 对象

  • route(rule, verify_func=None, no_parse_links=False)
装饰器, 在用户自定义解析方法使用

Args:
  rule: 处理path的正则表达式, 可以包含参数, 使用'<>'包住参数名就可以, 例如 '/page/<no>', 这样用户自定义方法可以接收no参数

  verify_func: 可选, 用户路径验证方法, 如果提供可以自定义是否处理该路径, 方法签名为 verify_url(path), return True or False

  no_parse_links: 可选, 是否处理该页面上的其他链接, 如果需要关闭解析该页面链接, 可以设置为True

用户被装饰方法定义如下:

@router.route('/page/<no>', verify_page_path)
def process_page(text, path, no):

Args:
    text: 当前要解析页面的html代码, 可以使用其他框架如lxml, request_html, BeautifulSoup
    path: 可选, 如果有该参数, 系统会自动传递该path参数供使用
    其他参数, 装饰器的自定义参数

    注意: 前两个参数为系统自动提供, 顺序不能错, 用户自定义参数在前两个之后

aspider 模块

该模块提供download方法启动爬虫

  • download(loop=None, extra_args=None):
Args:
  loop - 用户提供asyncio loop对象, 一般不需要, 除非是在编写定时任务, 多线程使用, 这时候用户生成一个loop对象并传入
  extra_args - 其他参数, aspider 命令行使用的参数可以用该dict对象传入
  其中, 必须的是roots key, 对应为抓取跟路径, 是一个list

Returns:
  stats - 抓取过程统计对象, 包含抓取的网页数量, 完成时间等数据

example:

  options = {
        'roots': ['https://movie.douban.com/top250']
    }
  stats = aspider.download(extra_args=options)

reporting 模块

  • Stats 对象 该对象记录抓取过程的统计数据, 如抓取网页数量, 完成时间, 速度等

  • report() 该方法打印这次抓取的记录报告

API 调用爬虫例子

douban top250 电影抓取(代码在example 下))

  1. 先取得router对象
  2. 定义解析方法, 使用router.route装饰器, 提供一个参数为要解析的path正则表达式
  3. 定义一个main方法启动爬虫就可以
'''
例子: douban top 250 电影名单爬取
'''
from collections import namedtuple
from aspider.routeing import get_router
from aspider import aspider
from requests_html import HTML

Movie = namedtuple('Movie', ['rank', 'score', 'title'])

router = get_router()

root_url = 'https://movie.douban.com/top250'

movies_250 = []


@router.route('/top250\?start.+')
def process_page(text):
    html = HTML(html=text)
    item_css = '#content  ol.grid_view > li'
    items = html.find(item_css)
    rank_css = 'em'
    title_css = '.info  span.title'
    score_css = '.info  .rating_num'
    for item in items:
        rank = int(item.find(rank_css, first=True).text)
        title = item.find(title_css, first=True).text
        score = float(item.find(score_css, first=True).text)
        movies_250.append(Movie(rank, score, title))


def main():
    options = {
        'roots': [root_url]
    }
    stats = aspider.download(extra_args=options)
    stats.report()
    fname = 'top250.txt'
    sorted_movies_250 = sorted(movies_250, key=lambda m: m.rank)
    with open(fname, 'w') as f:
        for movie in sorted_movies_250:
            print(f'#{movie.rank:<10} {movie.score:<10.2f} - {movie.title}')
            print(f'#{movie.rank:<10} {movie.score:<10.2f} - {movie.title}', file=f)


if __name__ == "__main__":
    main()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for aspider, version 0.1.2
Filename, size File type Python version Upload date Hashes
Filename, size aspider-0.1.2-py2.py3-none-any.whl (19.3 kB) File type Wheel Python version py2.py3 Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page