pyspider

pyspider简介

官方文档:http://docs.pyspider.org/

中文网址:http://www.pyspider.cn/book/pyspider/

最新版本: https://github.com/binux/pyspider/releases

PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI。采用Python语言编写,分布式架构,支持多种数据库后端,强大的WebUI支持脚本编辑器,任务监视器,项目管理器以及结果查看器。在线示例: http://demo.pyspider.org/

pyspider是作者之前做的一个爬虫架构的开源化实现。主要的功能需求是:

  • 抓取、更新调度多站点的特定的页面
  • 需要对页面进行结构化信息提取
  • 灵活可扩展,稳定可监控 而这也是绝大多数python爬虫的需求 —— 定向抓取,结构化化解析。但是面对结构迥异的各种网站,单一的抓取模式并不一定能满足,灵活的抓取控制是必须的。为了达到这个目的,单纯的配置文件往往不够灵活,于是,通过脚本去控制抓取是我最后的选择。 而去重调度,队列,抓取,异常处理,监控等功能作为框架,提供给抓取脚本,并保证灵活性。最后加上web的编辑调试环境,以及web任务监控,即成为了这套框架。

pyspider的设计基础是:以python脚本驱动的抓取环模型爬虫

  • 通过python脚本进行结构化信息的提取,follow链接调度抓取控制,实现最大的灵活性
  • 通过web化的脚本编写、调试环境。web展现调度状态
  • 抓取环模型成熟稳定,模块间相互独立,通过消息队列连接,从单进程到多机分布式灵活拓展

安装:

添加依赖

sudo apt-get install python python-dev python-distribute python-pip libcurl4-openssl-dev libxml2-dev libxslt1-dev python-lxml libssl-dev zlib1g-dev

sudo apt-get install phantomjs

pip3 install pyspider

启动:

pyspider all

编写一个pyspider项目
from pyspider.libs.base_handler import *
import pymongo,pymysql

class Handler(BaseHandler):
    """
    Handler 就是 pyspider 爬虫的主类,我
    们可以在此处定义爬取、解析、存储的逻辑。
    整个爬虫的功能只需要一个 Handler 即可完成
    """

    #crawl_config 属性。我们可以将本项目的
    #所有爬取配置统一定义到这里,如定义 
    #Headers、设置代理等,配置之后全局生效
    crawl_config = {
    }
    
    #mongodb数据库连接
    mongocli = pymongo.MongoClient(host='localhost', port=27017)
    db = mongocli['jobbole']
    jobbolearticle = db['jobbolearticle']

    #mysql数据库连接
    client = pymysql.Connect(
        'localhost','root','ljh1314',
        'class1804',charset='utf8'
    )
    cursor = client.cursor()

    #on_start() 方法是爬取入口,初始的爬取
    #请求会在这里产生
    @every(minutes=24 * 60)
    def on_start(self):
        self.crawl('http://blog.jobbole.com/all-posts/', callback=self.index_page)

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        print(response)
        for each in response.doc('a[class="page-numbers"]').items():
            self.crawl(each.attr.href, callback=self.detail_page)

    @config(priority=2)
    def detail_page(self, response):
        
        self.index_page(response)
        
        list = response.doc('#archive .post.floated-thumb')
       # print(list)
        for item in list.items():
            print('拿到了数据')
            print(type(item))
            title = item('a.archive-title').text()
            url = item('a.archive-title').attr.href
            print(title,url)
            return {
                'title':title,
                'url':url,
            }

    #方法中return的结果会执行on_result
    def on_result(self,result):
        #可以在这里做数据的持久化
        print(result)

        #mysql数据库存储
        sql = """
        INSERT INTO jobbole()
        VALUE (%s,%s)
        """
        
        try:
            self.cursor.execute(sql,[result['title'],result['url']])
            self.client.commit()
        except Exception as err:
            print(err)
            self.client.rollback()

        #mongodb数据库存储
        self.jobbolearticle.insert(result)
链家网二手房例子

# !/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2019-01-09 14:03:21
# Project: lianjia

from pyspider.libs.base_handler import *
import json
import pymongo
import pymysql


class Handler(BaseHandler):
    # pyspider爬虫的主类,在这里进行爬取解析和存储数据

    # crawl_config:在这个参数中可以做全局的设置(UA,Header,proxy)
    crawl_config = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
    }

    # 创建mongodb数据库连接
    mongo_cli = pymongo.MongoClient('127.0.0.1', 27017)
    # 获取要操作的数据库
    db = mongo_cli['lianjia']
    # 获取数据库下的集合
    col = db['lianjiacol']

    # 创建mysql连接
    # mysql_cli = pymysql.Connect('127.0.0.1','root','nihao123','lianjia',3306,charset='utf8')
    # cursor = mysql_cli.cursor()

    # 定时每隔一天进行重复请求,重新执行on_start方法
    @every(minutes=24 * 60)
    def on_start(self):
        # 根据crawl发起请求
        self.crawl('https://bj.lianjia.com/ershoufang/', callback=self.index_page)

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):

        # response.doc 是一个pyquery对象
        # response.etree:返回的是一个lxml对象,可以使用xpath语法
        # 提取每一个房源的详情地址url
        hourse_infos = response.doc('ul.sellListContent li.clear.LOGCLICKDATA')
        for hourse in hourse_infos.items():

            detail_url = hourse('div.title a').attr.href
            self.crawl(detail_url, callback=self.detail_page)

        # 提取下一页发起请求
        # data = response.doc('div.page-box.house-lst-page-box').attr.page-data
        data = response.etree.xpath('//div[@class="page-box house-lst-page-box"]/@page-data')[0]
        print('data', data)
        json_data = json.loads(data)
        # print('nextdata',json_data)
        cur_page = int(json_data['curPage'])
        total_page = int(json_data['totalPage'])
        if cur_page < total_page:
            # 发起下一页请求
            next_page = cur_page + 1
            next_url = 'https://bj.lianjia.com/ershoufang/pg%s/' % str(next_page)
            self.crawl(next_url, callback=self.index_page)

        # next_url = response.doc()
        # for each in response.doc('a[href^="http"]').items():
        # self.crawl(each.attr.href, callback=self.detail_page)

    @config(priority=2)
    def detail_page(self, response):
        print('二手房详情获取成功')
        # 获取详情的数据
        info = {}
        # 标题(获取标签的属性和文本)
        # info['title'] = response.doc('h1.main').attr.title
        info['title'] = response.doc('h1.main').text()
        # 描述
        info['sub_title'] = response.doc('div.sub').text()
        # 关注人数
        info['attenNum'] = response.doc('#favCount').text()
        # 预约看房
        info['yuyueNum'] = response.doc('#cartCount').text()
        # 总价
        info['price'] = response.doc('div.price.total').text()
        # 每平
        info['unitPrice'] = response.doc('span.unitPriceValue').text()
        # 规格
        info['room'] = response.doc('div.room div.mainInfo').text()
        # 朝向
        info['type'] = response.doc('div.type div.mainInfo').text()
        # 面积
        info['area'] = response.doc('div.area div.mainInfo').text()
        # 小区名称
        info['aroundinfo'] = response.doc('div.communityName span.label').text()

        # print(info)

        return info

    def on_result(self, result):
        print('获取到了结果', result)
        if result:
            try:
                self.col.insert(result)
                print('数据存储成功')

                # sql = """
                # insert into lianjiadb(%s) values(%s)
                # """
                # %(','.join(result.keys()),
                # ','.join(['%s']*len(result)))
                # data = list(result.value())


            except Exception as err:
                print('数据插入失败', err)

        # return {
        # "url": response.url,
        # "title": response.doc('title').text(),
        # }

你可能感兴趣的:(pyspider)