所在位置:

用scrapy爬取古诗词网站

今天我们用另外一种方式来爬取古诗词的网站,目标网站是爬取诗词名句网的所有古诗词,相比于我们之前爬取博客的方法会更简单些,但要对正则表达式有一定的了解

在本文中,可以学到

  • CrawlSpider的用法
  • settings.py的常用配置
  • 如何使用随机浏览器代理
  • 如何使用代理ip
  • 如何把爬取的数据保存到数据库中

预备工作

  • python2.7.10
  • scrapy 1.4.0
  • virtualenv 15.2.0
  • pip install mysql-python

scrapy的架构图

Scrapy中的数据流由执行引擎控制,其过程如下:

1.引擎打开一个网站,找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)

2.引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度

3.引擎向调度器请求下一个要爬取的URL

4.调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)

5.一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。

6.引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理

7.Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎

8.引擎将(Spider返回的)爬取到的Item给ItemPipeline,将(Spider返回的)Request给调度器

9.(从第二步)重复直到调度器中没有更多地request,引擎关闭该网站

总结:绿色的箭头是数据流向,有两种情况,这两种情况是可以并行的:

  • 存放url->发送请求->提取url->存放url->发送请示,不停地循环

  • 存放url->发送请求-> 提取数据-> 数据队列处理

创建项目

scrapy startproject gushi
cd gushi
scrapy genspider gushici www.shicimingju.com

项目结构

设置浏览器代理

  • 设置固定的浏览器代理

把 settings.py 文件里的 USER_AGENT 选项修改为:

USER_AGENT = '(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6'
  • 设置随机的浏览器代理

在gushi的目录下新建一个useragent.py文件,内容如下:

import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware


class UserAgent(UserAgentMiddleware):
    user_agent_list = [
    '(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:70.0) Gecko/20100101 Firefox/70.0'
      ]

    def __init__(self, user_agent=''):
        self.user_agent = user_agent

    def process_request(self, request, spider):
        user_agent = random.choice(self.user_agent_list)
        if user_agent:
            print '==随机的浏览器=='
            print user_agent
            print '===='
            request.headers.setdefault('User-Agent', user_agent)

修改 settings.py 文件的 DOWNLOADER_MIDDLEWARES 选项

DOWNLOADER_MIDDLEWARES = {
    'gushi.useragent.UserAgent': 1
}

设置代理ip

在gushi的目录下新建一个proxymiddle.py文件,内容如下:

-*- coding: utf-8 -*-

import random


class ProxyMiddle(object):
    proxy_list = [
        '163.125.114.217:8088'
    ]

    def process_request(self, request, spider):
        proxy_url = random.choice(self.proxy_list)
        if proxy_url:
            print '====代理地址====='
            print proxy_url
            print '================='
            request.meta['proxy'] = 'http://' + proxy_url

修改 settings.py 文件的 DOWNLOADER_MIDDLEWARES 选项

DOWNLOADER_MIDDLEWARES = {
    'gushi.useragent.UserAgent': 1,
    'gushi.proxymiddleware.ProxyMiddle': 10
}

修改创建的爬虫类

  • 进入到爬虫类的目录
cd gushi/gushi/spiders/
  • 修改 gushici.py 的内容如下
# -*- coding: utf-8 -*-

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class GushiciSpider(CrawlSpider):
    name = 'gushici'
    allowed_domains = ['www.shicimingju.com']
    start_urls = ['http://www.shicimingju.com/']

    rules = (
        Rule(LinkExtractor(allow=r'chaxun/list/\d+.html$'),
        callback='parse_item', follow=True),
      )

    def parse_item(self, response):
        print '======='
        print response.url
        print '======='

这里我们只是测试一下能不能爬取类似于chaxun/list/132434.html的所有文章,rules用来定义要爬取页面的规则,follow=True表示用递归来爬取,callback就是用来处理爬取到的页面,后面的逻辑就在这里操作

运行爬虫

scrapy crawl --nolog  gushici

保存爬取指定内容到数据库

scrapy把爬取的内容保存到数据库要经过下面三步:

  • 先处理要爬取的具体字段
  • 在items.py定义要爬取的具体字段
  • 在pipelines.py里把爬取的内容插入到数据库

这三个步骤如下:

  • 修改 gushici.py 的内容如下:
# -*- coding: utf-8 -*-

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from gushi.items import GushiItem


class GushiciSpider(CrawlSpider):
    name = 'gushici'
    allowed_domains = ['www.shicimingju.com']
    start_urls = ['http://www.shicimingju.com/']

    rules = (
        Rule(LinkExtractor(allow=r'chaxun/list/\d+.html$'),
             callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        gushiItem = GushiItem()
        title = response.css('#item_div h1::text').extract_first('')
        dynasty = response.css(
            '#item_div .niandai_zuozhe::text').extract_first('')
        author = response.css(
            '#item_div .niandai_zuozhe a::text').extract_first('')
        content = response.css(
            '#item_div .item_content::text').extract_first('')
        tag = response.css('#item_div .shici-mark a::text').extract()
        tag = ','.join(tag)
        dynasty = ''.join(dynasty)
        gushiItem['title'] = title
        gushiItem['dynasty'] = dynasty
        gushiItem['content'] = content
        gushiItem['author'] = author
        gushiItem['tag'] = tag
        yield gushiItem
  • 修改 items.py 的内容如下:
# -*- coding: utf-8 -*-

import scrapy


class GushiItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()              # 标题
    dynasty = scrapy.Field()            # 朝代
    author = scrapy.Field()             # 作者
    content = scrapy.Field()            # 内容
    tag = scrapy.Field()                # 标签
  • 修改 pipelines.py 的内容如下:
# -*- coding: utf-8 -*-

import MySQLdb
import MySQLdb.cursors
from settings import MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB


class GushiPipeline(object):
    def __init__(self):
        self.conn = MySQLdb.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD,
                                    MYSQL_DB, charset='utf8')
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
        title = item['title']
        dynasty = item['dynasty']
        author = item['author']
        content = item['content']
        tag = item['tag']

        self.cursor.execute(
            "SELECT 1 FROM article WHERE title=%s", (title,))
        ret = self.cursor.fetchone()

        if not ret:
            sql = """
                INSERT INTO article(title, dynasty, author, content, tag) VALUES(%s, %s, %s, %s, %s)
            """
            self.cursor.execute(
                sql, (title, dynasty, author, content, tag))
            self.conn.commit()

        return item

    def spider_closed(self, spider):
        self.cursor.close()
        self.conn.close()
  • 新建一个数据库,同时新建一个article的表和字段

  • 修改 settings.py 文件,修改和添加下面内容:

ITEM_PIPELINES = {
    'gushi.pipelines.GushiPipeline': 300,
}

MYSQL_HOST = 'localhost'
MYSQL_USER = 'root'
MYSQL_PASSWORD = 'password'
MYSQL_DB = 'shicimingju'

再次运行爬虫

scrapy crawl --nolog gushici

参考链接

【上一篇】如何使用Python Scrapy开发你的第一个网页爬虫【翻译】

【下一篇】git常用命令