Scrapy 简单入门

- 5 mins

使用爬虫有段时间了,最初爬虫是自学的,封装过一个简单得天气APP,在上一家公司时,就常用爬取的数据来支撑公司的一些核心业务,之前更多用的是 urllib + BeautifoulSoup 的方式来抓取和解析数据,但是用久了就觉得自己的技术思维固化,最近研究了一下 Scrapy 的实现。

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 【来自Scrapy文档】

对于写爬虫项目,核心是找到要提取数据的标准格式即文档中提到的结构性数据,从而可以应用一个爬虫可以不限时地爬取想要的所有数据而不需要去更改代码。

里面运用的xpath,可以参考 阮一峰老师的博客 ,深入浅出,很容易学会理解。

爬虫原理

通过网络请求,读取目标网站的数据,然后根据返回数据的结构来抓到自己所需的数据。

1、发起网络请求

2、读取目标网站的回应数据

3、从回应数据中,获取到目标信息

4、对信息再次处理

安装Scrapy

可以采用pip方式安装,也可以通过源码安装。使用pip的好处,是在安装插件时,能将依赖一起安装。

pip install scrapy

创建 Scrapy 应用

通Python第三方框架一样,有一个startproject命令可以用来创建项目。

scrapy startprojecty tuition

在scrapy安装正常的情况下,会出现一个tuition目录,即爬虫的项目目录。

开始编写爬虫

进入 tuition/tuition/spiders 目录,然后创建一个 tuition_spider.py 文件,注意文件名不要与项目重名,否则会出现一些引入文件的错误。

先写py文件的头部和引入相关依赖。

#!/usr/bin/python
#encoding=utf-8

import scrapy, sys, re

reload(sys)
sys.setdefaultencoding('utf-8') # 一般对于中文的处理,要重新加载默认编码

然后开始写核心的爬取方法,以爬取华尔街见闻新闻首页为例。

class TuitionSpider(scrapy.Spider):
    name = "tuition"
    allowed_domains = ['wallstreetcn.com'] # 这里是目标数据的来源网站域名

    def __init__(self):
        self.start_urls = ['https://wallstreetcn.com/news/global'] # 模板数据的来源网址,可填写多个

    def parse(self, response): # scrapy项目第一次网络请求的默认解析函数
        home_page_link = response.xpath("//div[@class='article-entry list-item']//a[@class='article-link title']//@href").extract() # xpath 方式来解析目标数据,读取所有class属性为article-entry list-item的div标签中的href属性值 
        for news in home_page_link: # 遍历所有的新闻链接,并准备第二次网络请求
            if re.match(r'^/articles/?\w.+$', news): # 过滤URL格式
                news_link = 'https://wallstreetcn.com%s'%str(news); # 链接拼接
                yield scrapy.Request(news_link, callback = self.wallstreet_news) # 发送第二次网络请求

    def wallstreet_news(self, response): # 第二次网络请求的解析函数
        title = response.xpath("//h1[@class='title']//text()").extract_first() # 提取第一条class属性值为title的h1标签值的文字内容
        news = response.xpath("//div[@class='rich-text']").extract()
        print title
        print news

说明

最后在项目根目录下执行

scrapy crawl tuition

程序会执行并打印出爬虫的各个指标值和提取到的数据。

高级方法

User-Agent 设置

settings.py

USER_AGENT = 'tuition (+http://www.yourdomain.com)'

可以设置为任意Agent,可以减少被服务器拒绝的次数,如

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'

重写 Scrapy 默认的请求函数,在请求中加上 header 和cookie 等所需参数,比如抓取雪球的新闻数据,需要在请求中添加header以及cookie信息

def start_requests(self):
        headers = {
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36',
            'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'Referer':'https://xueqiu.com',
            'Host':'xueqiu.com'
        }
        cookie = {
            'device_id':'6ed816cd2373d60aeaa93a3c79cedb2b',
            's':'f716n5qev9',
            'bid':'0aa20bea525418b1b4293f02f9f23c90_jty6nfgl',
            'xq_a_token':'e71fc3865ba97b436732bbba8f9e9cbd74501c3b',
            'xqat':'e71fc3865ba97b436732bbba8f9e9cbd74501c3b',
            'xq_r_token':'5c020cfae733c5ba38e05f73e0dcedf2f2fac088',
            'xq_token_expire':'Mon%20Jun%2003%202019%2015%3A07%3A01%20GMT%2B0800%20(CST)',
            'xq_is_login':'1',
            'u':'2017864747',
            'aliyungf_tc':'AQAAAKlkWELEuAAAxIrqe786K+m5asGr',
            'Hm_lvt_1db88642e346389874251b5a1eded6e3':'1557998440,1558076670,1558321777,1558492009',
            'Hm_lpvt_1db88642e346389874251b5a1eded6e3':'1558517173'
        }
        yield scrapy.FormRequest(self.start_urls, method='GET', cookies=cookie, headers=headers, dont_filter=True, callback=self.parse_json) # 可自由设置回调函数

爬取到的数据保存至数据库

配置Item

找到项目的items.py文件,添加所要提取的数据字段。

import scrapy

class TuitionItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    news = scrapy.Field()

找到pipelines.py文件,配置数据库信息。

# 引入必要的依赖
from twisted.enterprise import adbapi
import MySQLdb
import MySQLdb.cursors
from MySQLdb import escape_string
from scrapy.crawler import Settings as settings

class TuitionPipeline(object):
    def __init__(self): # 数据库配置
        dbargs = dict(
            host = '127.0.0.1' ,
            db = 'tuition',
            user = 'root', #replace with you user name
            passwd = '123', # replace with you password
            charset = 'utf8',
            cursorclass = MySQLdb.cursors.DictCursor,
            use_unicode = True,
        )    
        self.dbpool = adbapi.ConnectionPool('MySQLdb',**dbargs)

    def process_item(self, item, spider): # 默认数据处理函数
        res = self.dbpool.runInteraction(self.update_firms_data,item)
        return item

    def update_firms_data(self, conn, item): # 自定义数据处理
        conn.execute('insert into `news` (`title`, `news`) values (%s, %s)', (escape_string(item['title']), escape_string(item['news']))) # 可直接执行SQL语句

修改 settings.py,将下面这一行的注释取消,开启数据管道。

ITEM_PIPELINES = {
   'tuition.pipelines.TuitionPipeline': 300,
}

在主爬虫文件中引入Item。

from tuition.items import TuitionItem

修改解析函数,将item数据提交到数据处理管道。

def wallstreet_news(self, response): 
        title = response.xpath("//h1[@class='title']//text()").extract_first()
        news = response.xpath("//div[@class='rich-text']").extract()
        item = TuitionItem()
        item['title'] = title
        item['news'] = news
        yield item # 提交至数据管道处理数据

最后执行爬虫命令,数据就会爬取保存到数据库中。

rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora