scrapy爬虫框架

VogtEmma 发布于9月前

scrapy框架是异步处理框架,可配置和可扩展程度非常高,Python中使用最广泛的爬虫框架。

安装

Ubuntu安装

1、安装依赖包

  1. sudo apt-get install libffi-dev
  2. sudo apt-get install libssl-dev
  3. sudo apt-get install libxml2-dev
  4. sudo apt-get install python3-dev
  5. sudo apt-get install libxslt1-dev
  6. sudo apt-get install zlib1g-dev
  7. sudo pip3 install -I -U service_identity

2、安装scrapy框架

  1. sudo pip3 install Scrapy

Windows安装

cmd命令行(管理员): python -m pip install Scrapy

Scrapy框架五大组件

  1. 引擎(Engine)      :整个框架核心
  2. 调度器(Scheduler)   :维护请求队列
  3. 下载器(Downloader)  :获取响应对象,下载器是基于多线程的
  4. 爬虫文件(Spider)   :数据解析提取
  5. 项目管道(Pipeline)  :数据入库处理

下载器中间件(Downloader Middlewares) : 引擎->下载器,包装请求(随机代理等)

蜘蛛中间件(Spider Middlewares) : 引擎->爬虫文件,可修改响应对象属性

scrapy爬虫框架

scrapy爬虫工作流程

爬虫项目启动

1、由引擎向爬虫程序索要第一个要爬取的URL,交给调度器去入队列

2、调度器处理请求后出队列,通过下载器中间件交给下载器去下载

3、下载器得到响应对象后,通过蜘蛛中间件交给爬虫程序

4、爬虫程序进行数据提取:

1、数据交给管道文件去入库处理

2、对于需要继续跟进的URL,再次交给调度器入队列,依次循环

scrapy常用命令

1、创建爬虫项目 scrapy startproject 项目名

2、创建爬虫文件 scrapy genspider 爬虫名 域名

域名为协议后面的名字

3、运行爬虫 scrapy crawl 爬虫名

在cmd窗口运行上面指令后,会在当前文件夹自动创建如下目录结构。

scrapy项目目录结构

Baidu                   # 项目文件夹

├── Baidu               # 项目目录

│   ├── items.py        # 定义数据结构

│   ├── middlewares.py    # 中间件

│   ├── pipelines.py     # 数据处理

│   ├── settings.py      # 全局配置

│   └── spiders

│       ├── baidu.py    # 爬虫文件

└── scrapy.cfg           # 项目基本配置文件

全局配置文件settings.py详解

1、定义User-Agent

USER_AGENT = 'Mozilla/5.0'

2、是否遵循robots协议,一定要设置为False

ROBOTSTXT_OBEY = False

3、最大并发量,默认为16

CONCURRENT_REQUESTS = 32

4、下载延迟时间

DOWNLOAD_DELAY = 1

5、请求头,此处也可以添加User-Agent

DEFAULT_REQUEST_HEADERS={}

6、项目管道,运行管道函数

ITEM_PIPELINES={

'项目目录名.pipelines.类名':300

}

创建爬虫项目步骤

  1. 新建项目 :scrapy startproject 项目名
  2. cd 项目文件夹
  3. 新建爬虫文件 :scrapy genspider 文件名 域名
  4. 明确目标(items.py)
  5. 写爬虫程序(文件名.py)
  6. 管道文件(pipelines.py)
  7. 全局配置(settings.py)
  8. 运行爬虫 :scrapy crawl 爬虫名

pycharm运行爬虫项目

1、创建一个脚本文件,比如:begin.py(和scrapy.cfg文件同目录)

2、begin.py中内容:

from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())

导入cmd命令行模块,在python中写cmd命令,之所以用split()是因为把字符串按空格切割,这样cmd才能识别是3个参数。

百度

目标: 打开百度首页,把 '百度一下,你就知道' 抓取下来,从终端输出

实现步骤

1、创建项目Baidu 和 爬虫文件baidu

1、scrapy startproject Baidu

2、cd Baidu

3、scrapy genspider baidu www.baidu.com

2、编写爬虫文件baidu.py,xpath提取数据

# -*- coding: utf-8 -*-
import scrapy


class BaiduSpider(scrapy.Spider):
    name = 'baidu'      # 爬虫名 : scrapy crawl 爬虫名
    allowed_domains = ['www.baidu.com']     # 允许爬取的域名
    start_urls = ['http://www.baidu.com/']      # 起始URL地址

    def parse(self, response):
        # response为百度的响应对象,提取"百度一下,你就知道"
        # r_list: [<selector xpath='',data=''>]
        # extract(): ["百度一下,你就知道"]
        # extract_first(): "百度一下,你就知道"
        # 1.6版本后可使用get(): "百度一下,你就知道"
        r_list = response.xpath('/html/head/title/text()').get()

        print('*'*50)
        print(r_list)
        print('*'*50)

3、全局配置settings.py

USER_AGENT = 'Mozilla/5.0'
ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}

或者把USER_AGENT文件写道头文件里面

DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'USER_AGENT':'Mozilla/5.0',
}

4、创建begin.py(和scrapy.cfg同目录)

from scrapy import cmdline

cmdline.execute('scrapy crawl baidu'.split())

5、启动爬虫

直接运行 begin.py 文件即可

猫眼电影案例

目的

  • URL: 百度搜索 -> 猫眼电影 -> 榜单 -> top100榜
  • 爬取内容:电影名称、电影主演、上映时间

实现步骤

1、创建项目和爬虫文件

创建爬虫项目scrapy startproject Maoyan
cd Maoyan
创建爬虫文件scrapy genspider maoyan maoyan.com

2、定义要爬取的数据结构(items.py)

name = scrapy.Field()
star = scrapy.Field()
time = scrapy.Field()

3、编写爬虫文件(maoyan.py)

1、基准xpath,匹配每个电影信息节点对象列表

dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')

2、for dd in dd_list:

电影名称 = dd.xpath('./a/@title')

电影主演 = dd.xpath('.//p[@class="star"]/text()')

上映时间 = dd.xpath('.//p[@class="releasetime"]/text()')

代码实现一

下载速度慢,爬了一页再爬第二页,调度器里面只有一个地址。

# -*- coding: utf-8 -*-
import scrapy
from ..items import MaoyanItem


class MaoyanSpider(scrapy.Spider):
    name = 'maoyan'  # 爬虫名
    allowed_domains = ['maoyan.com']  # 允许爬虫的域名
    start_urls = ['https://maoyan.com/board/4?offset=0']  # 起始的URL地址
    offset = 0

    def parse(self, response):
        # 给items.py中的类:MaoyanItem(scrapy.Item)实例化
        item = MaoyanItem()

        # 基准xpath,匹配每个电影信息节点对象列表
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # 依次遍历
        for dd in dd_list:
            # [<selector xpath='' data='霸王别姬'>]
            # dd.xpath('')结果为[选择器1,选择器2]
            # .extract() 把[选择器1,选择器2]所有选择器序列化为unicode字符串
            # .extract_first() : 取第一个字符串
            # 是在给items.py中那些类变量赋值
            item['name'] = dd.xpath('./a/@title').get().strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()').get().strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').get().strip()

            # 把item对象交给管道文件处理
            yield item

        # 此方法不推荐,效率低
        self.offset += 10
        if self.offset <= 91:
            url = 'https://maoyan.com/board/4?offset={}'.format(self.offset)
            # 交给调度器入队列
            yield scrapy.Request(
                url=url,
                callback=self.parse)

代码实现二,基于下载器是多线程的,把多个地址,一次性的都给调度器,请求指纹,第一个地址爬了两次

# -*- coding: utf-8 -*-
import scrapy
from ..items import MaoyanItem


class MaoyanSpider(scrapy.Spider):
    name = 'maoyan2'  # 爬虫名
    allowed_domains = ['maoyan.com']  # 允许爬取的域名
    start_urls = ['https://maoyan.com/board/4?offset=0']  # 起始的URL地址

    def parse(self, response):
        for offset in range(0, 91, 10):
            url = 'https://maoyan.com/board/4?offset={}'.format(offset)
            # 把地址交给调度器入队列
            yield scrapy.Request(url=url,
                                 callback=self.parse_page)

    def parse_page(self, response):
        # 给items.py中的类:MaoyanItem(scrapy.Item)实例化
        item = MaoyanItem()

        # 基准xpath,匹配每个电影信息节点对象列表
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # dd_list : [<element dd at xxx>,<...>]

        for dd in dd_list:
            # [<selector xpath='' data='霸王别姬'>]
            # dd.xpath('')结果为[选择器1,选择器2]
            # .extract() 把[选择器1,选择器2]所有选择器序列化为
            # unicode字符串
            # .extract_first() : 取第一个字符串
            # 是在给items.py中那些类变量赋值
            item['name'] = dd.xpath('./a/@title').get().strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()').get().strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').get().strip()

            # 把item对象交给管道文件处理
            yield item

代码实现三

# 重写start_requests()方法,直接把多个地址都交给调度器去处理
import scrapy
from ..items import MaoyanItem


class MaoyanSpider(scrapy.Spider):
    name = 'maoyan3'  # 爬虫名
    allowed_domains = ['maoyan.com']  # 允许爬取的域名

    # 去掉start_urls变量

    # 重写start_requests()方法,把所有URL地址都交给调度器
    # 去掉start_urls

    def start_requests(self):
        for offset in range(0, 91, 10):
            url = 'https://maoyan.com/board/4?offset={}'.format(offset)
            yield scrapy.Request(url=url, callback=self.parse)  # 把地址交给调度器入队列

    def parse(self, response):
        item = MaoyanItem()  # 给items.py中的类:MaoyanItem(scrapy.Item)实例化

        # 基准xpath
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # 依次遍历
        for dd in dd_list:
            # [<selector xpath='' data='霸王别姬'>]
            # dd.xpath('')结果为[选择器1,选择器2]
            # .extract() 把[选择器1,选择器2]所有选择器序列化为
            # unicode字符串
            # .extract_first() : 取第一个字符串
            # 是在给items.py中那些类变量赋值
            item['name'] = dd.xpath('./a/@title').get().strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()').get().strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').get().strip()

            yield item  # 把item对象交给pipline管道文件处理

3、定义管道文件(pipelines.py)

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
import pymysql
from .settings import *


class MaoyanPipeline(object):
    # item: 从爬虫文件maoyan.py中yield的item数据
    def process_item(self, item, spider):
        print(item['name'], item['time'], item['star'])

        return item


# 新建自定义管道 - 存入MySQL数据库
class MaoyanMysqlPipeline(object):
    # 爬虫项目开始运行时执行此函数
    def open_spider(self, spider):
        print('我是open_spider函数输出')
        # 一般用于建立数据库连接
        self.db = pymysql.connect(
            host=MYSQL_HOST,
            user=MYSQL_USER,
            password=MYSQL_PWD,
            database=MYSQL_DB,
            charset=MYSQL_CHAR)
        self.cursor = self.db.cursor()

    def process_item(self, item, spider):
        # 因为execute()的第二个参数为列表
        L = [item['name'].strip(),
             item['star'].strip(),
             item['time'].strip()]
        self.cursor.execute('insert into filmtab values(%s,%s,%s)', L)
        self.db.commit()  # 提交到数据库

        return item

    # 爬虫项目结束时执行此函数,只执行一次
    def close_spider(self, spider):
        print('我是close_spider函数输出')
        # 一般用于断开数据库连接
        self.cursor.close()
        self.db.close()

5、全局配置文件(settings.py)

ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    '’USER_AGENT' = 'Mozilla/5.0'
}
ITEM_PIPELINES = {
    # 300表示优先级(1-1000),数字越小,优先级越高
    'Maoyan.pipelines.MaoyanPipeline': 300,
    'Maoyan.pipelines.MaoyanMysqlPipeline': 200}

6、创建并运行文件(begin.py)

from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())

爬虫项目启动方式

方式一

从爬虫文件(spider)的start_urls变量中遍历URL地址,把下载器返回的响应对象(response)交给爬虫文件的parse()函数处理

# start_urls = ['http://www.baidu.com/']

方式二

重写start_requests()方法,从此方法中获取URL,交给指定的callback解析函数处理

1、去掉start_urls变量

2、def start_requests(self):

# 生成要爬取的URL地址,利用scrapy.Request()方法交给调度器 **

知识点汇总

response.xpath('')调用方法

结果:列表,元素为选择器 ['<selector xpath='' data='A'>]

.extract() :提取文本内容,序列化列表中所有选择器为Unicode字符串 ['A','B','C']

.extract_first() 或者 get() :获取列表中第1个序列化的元素(字符串)

.get():提取列表中第1个文本内容

response.text:获取响应内容

response.body:获取bytes数据类型

response.xpath('')

pipelines.py中必须由1个函数叫process_item

def process_item(self,item,spider):
    return item ( * 此处必须返回 item )

日志变量及日志级别(settings.py)

# 日志相关变量

LOG_LEVEL = ''

LOG_LEVEL = 'INFO'# 表示终端只显示INFO和INF日志级别以上的信息,DEBUG就不会显示了

LOG_FILE :本来应该输出在终端的信息,写入到了log日志文件中

LOG_FILE = '文件名.log'

# 日志级别

5 CRITICAL :严重错误

4 ERROR    :普通错误

3 WARNING  :警告

2 INFO     :一般信息

1 DEBUG    :调试信息

settings.py常用变量

LOG_LEVEL = ''              # 1、设置日志级别
LOG_FILE = ''               # 2、保存到日志文件(不在终端输出)
FEED_EXPORT_ENCODING = ''   # 3、设置数据导出编码(主要针对于json文件)
IMAGES_STORE = '路径'        # 4、非结构化数据存储路径
USER_AGENT = ''             # 5、设置User-Agent
CONCURRENT_REQUESTS = 32    # 6、设置最大并发数(默认为16)

# 7、下载延迟时间(每隔多长时间请求一个网页)
# DOWNLOAD_DELAY 会影响 CONCURRENT_REQUESTS,不能使并发显现
# 有CONCURRENT_REQUESTS,没有DOWNLOAD_DELAY: 服务器会在同一时间收到大量的请求
# 有CONCURRENT_REQUESTS,有DOWNLOAD_DELAY 时,服务器不会在同一时间收到大量的请求
DOWNLOAD_DELAY = 3

DEFAULT_REQUEST_HEADERS = {}    # 8、请求头
ITEM_PIPELINES = {}             # 9、添加项目管道
DOWNLOADER_MIDDLEWARES = {}     # 10、添加下载器中间件

管道处理数据流程

1、在爬虫文件中为items.py中类做实例化,用爬下来的数据给对象赋值

from ..items import MaoyanItem

item = MaoyanItem()

item['name'] = xxx

2、管道文件(pipelines.py)

3、开启管道(settings.py)

ITEM_PIPELINES = { '项目目录名.pipelines.类名':优先级 }

优先级1-1000,数字越小优先级越高

scrapy.Request()参数

1、url

2、callback

3、meta:传递数据,定义代理

数据持久化存储(MySQL)

实现步骤

1、在setting.py中定义MYSQL相关变量

# 定义MySQL相关变量
MYSQL_HOST = '127.0.0.1'
MYSQL_USER = 'root'
MYSQL_PWD = '123456'
MYSQL_DB = 'maoyandb'
MYSQL_CHAR = 'utf8'

2、pipelines.py中新建管道类,并导入settings模块from .settings import *

# 新建自定义管道 - 存入MySQL数据库
class MaoyanMysqlPipeline(object):
    # 爬虫程序启动时,只执行1次,一般用于数据库连接
    def open_spider(self, spider):
        print('我是open_spider函数输出')
        # 一般用于建立数据库连接
        self.db = pymysql.connect(
            host=MYSQL_HOST,
            user=MYSQL_USER,
            password=MYSQL_PWD,
            database=MYSQL_DB,
            charset=MYSQL_CHAR)
        self.cursor = self.db.cursor()

    def process_item(self, item, spider):
        # 用于处理爬取的item数据,这个函数一定要有
        # 因为execute()的第二个参数为列表
        L = [item['name'].strip(),
             item['star'].strip(),
             item['time'].strip()]
        self.cursor.execute('insert into filmtab values(%s,%s,%s)', L)
        self.db.commit()    # 提交到数据库

        return item

    # 爬虫项目结束时执行此函数,只执行一次,一般用于断开数据库连接
    def close_spider(self, spider):
        print('我是close_spider函数输出')
        # 一般用于断开数据库连接
        self.cursor.close()
        self.db.close()

注意 :process_item() 函数中一定要 return item

3、settings.py中添加此管道

ITEM_PIPELINES = {
    'Maoyan.pipelines.MaoyanPipeline': 300,
    'Maoyan.pipelines.MaoyanMysqlPipeline': 200  # 数据库的管道
 }

注意 :process_item() 函数中一定要 return item, 因为第一个管道返回的item会继续交由下一个管道处理,否则返回并传入下一个管道的值为None

保存为csv、json文件

scrapy crawl maoyan -o maoyan.csv

scrapy crawl maoyan -o maoyan.json

# 在存json文件的时候,要在setting.py设置到处编码 FEED_EXPORT_ENCODING = 'utf-8'

盗墓笔记小说抓取案例(三级页面)

目标

# 抓取目标网站中盗墓笔记1-8中所有章节的所有小说的具体内容,保存到本地文件

1、网址 :http://www.daomubiji.com/

准备工作xpath

1、一级页面xpath(此处响应做了处理):

盗墓笔记1-8的链接://ul[@class="sub-menu"]/li/a/@href

2、二级页面xpath:/html/body/section/div[2]/div/article

基准xpath ://article

链接:./a/@href

标题:./a/text()# 七星鲁王 第一章 血尸

3、三级页面xpath:

response.xpath('//article[@class="article-content"]//p/text()').extract()

项目实现

1、创建项目及爬虫文件

创建项目 :scrapy startproject Daomu

创建爬虫 :scrapy genspider daomu www.daomubiji.com

2、定义要爬取的数据结构(把数据交给管道)items.py

import scrapy
​
class DaomuItem(scrapy.Item):
    juan_name = scrapy.Field()    # 卷名
    zh_num = scrapy.Field()       # 章节数
    zh_name = scrapy.Field()      # 章节名
    zh_link = scrapy.Field()      # 章节链接
    zh_content = scrapy.Field()   # 小说内容

3、爬虫文件实现数据抓取 daomu.py

# -*- coding: utf-8 -*-
import scrapy
from ..items import DaomuItem
​
class DaomuSpider(scrapy.Spider):
    name = 'daomu'
    allowed_domains = ['www.daomubiji.com']
    start_urls = ['http://www.daomubiji.com/']
​
    # 解析一级页面,提取 盗墓笔记1 2 3 ... 链接
    def parse(self, response):
        one_link_list = response.xpath('//ul[@class="sub-menu"]/li/a/@href').extract()
        print(one_link_list)
        # 把链接交给调度器入队列
        for one_link in one_link_list:
            yield scrapy.Request(url=one_link, callback=self.parse_two_link, dont_filter=True)
​
    # 解析二级页面
    def parse_two_link(self,response):
        # 基准xpath,匹配所有章节对象列表
        article_list = response.xpath('/html/body/section/div[2]/div/article')
        # 依次获取每个章节信息
        for article in article_list:
            # 创建item对象
            item = DaomuItem()
            info = article.xpath('./a/text()').extract_first().split()
            # info : ['七星鲁王','第一章','血尸']
            item['juan_name'] = info[0]
            item['zh_num'] = info[1]
            item['zh_name'] = info[2]
            item['zh_link'] = article.xpath('./a/@href').extract_first()
            # 把章节链接交给调度器
            yield scrapy.Request(
                url=item['zh_link'],
                # 把item传递到下一个解析函数
                meta={'item':item},
                callback=self.parse_three_link,
                dont_filter=True
            )
​
    # 解析三级页面
    def parse_three_link(self,response):
        # 获取上一个函数传递过来的item对象
        item = response.meta['item']
        # 获取小说内容
        # ['段落1','段落2','段落3',....]
        item['zh_content'] = '\n'.join(response.xpath(
          '//article[@class="article-content"]//p/text()'
        ).extract())

​        # 所有的数据都爬完了,再yield
        yield item
​
        # '\n'.join(['第一段','第二段','第三段'])

4、管道文件实现数据处理pipline.py

# -*- coding: utf-8 -*-
​
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
​
​
class DaomuPipeline(object):
    def process_item(self, item, spider):
        filename = '/home/tarena/aid1902/{}-{}-{}.txt'.format(
            item['juan_name'],
            item['zh_num'],
            item['zh_name']
        )
​
        f = open(filename,'w')
        f.write(item['zh_content'])
        f.close()
        return item

5、setting

打开通道

腾讯招聘

MySQL数据库--建库建表

create database tencentdb charset utf8;
use tencentdb;
create table tencenttab(
        name varchar(100),
        type varchar(100),
        duty varchar(5000),
        requirement varchar(5000)
        )charset=utf8;

1、创建项目+爬虫文件

scrapy startproject Tencent
cd Tencent
scrapy genspider tencent hr.tencent.com

2、定义爬取的数据结构 items.py

job_name = scrapy.Field()
job_type = scrapy.Field()    # 类别
job_duty = scrapy.Field()    # 职责
job_require = scrapy.Field()    # 要求
job_address = scrapy.Field()    # 地址

3、爬虫文件

class TencentSpider(scrapy.Spider):
    name = 'tencent'
    allowed_domains = ['careers.tencent.com']
    one_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn'
    two_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn'
    # 1. 去掉start_urls
    # 2. 重新start_requests()方法
    def start_requests(self):
        total_page = self.get_total_page()
        for page_index in range(1,total_page):
            url = self.one_url.format(page_index)
            yield scrapy.Request(
                url = url,
                callback = self.parse_one
            )
​
    # 获取总页数
    def get_total_page(self):
        url = self.one_url.format(1)
        html = requests.get(url=url).json()
        total_page = int(html['Data']['Count']) // 10 + 1
​
        return total_page
​
    # 解析一级页面函数
    def parse_one(self,response):
        html = json.loads(response.text)
        for job in html['Data']['Posts']:
            item = TencentItem()
            # postId: 拼接二级页面的地址
            post_id = job['PostId']
            two_url = self.two_url.format(post_id)
            # 交给调度器
            yield scrapy.Request(
                url = two_url,
                meta = {'item':item},
                callback = self.parse_two_page
            )
​
    def parse_two_page(self,response):
        item = response.meta['item']
        html = json.loads(response.text)
        item['job_name'] = html['Data']['RecruitPostName']
        item['job_type'] = html['Data']['CategoryName']
        item['job_duty'] = html['Data']['Responsibility']
        item['job_require'] = html['Data']['Responsibility']
        item['job_address'] = html['Data']['LocationName']
​
​
        yield item

4、管道文件

create database tencentdb charset utf8;
use tencentdb;
create table tencenttab(
        job_name varchar(500),
        job_type varchar(100),
        job_duty varchar(1000),
        job_require varchar(1000),
        job_address varchar(100)
        )charset=utf8;

管道文件pipelines实现

import pymysql
class TencentMysqlPipeline(object):
    def open_spider(self,spider):
        self.db = pymysql.connect(
            '127.0.0.1','root','123456','tencentdb',
            charset='utf8'
        )
        self.cursor = self.db.cursor()
​
    def process_item(self,item,spider):
        ins = 'insert into tencenttab values(%s,%s,%s,%s,%s)'
        job_list = [
            item['job_name'],item['job_type'],item['job_duty'],
            item['job_require'],item['job_address']
        ]
        self.cursor.execute(ins,job_list)
        self.db.commit()
        return item
​
    def close_spider(self,spider):
        self.cursor.close()
        self.db.close()

5、settings.py

定义常用变量,添加管道即可

图片管道(360图片抓取案例)

目标: www.so.com -> 图片 -> 美女

抓取网络数据包

2、F12抓包,抓取到json地址 和 查询参数(QueryString)

url = 'http://image.so.com/zj?ch=beauty&sn={}&listtype=new&temp=1'.format(str(sn))

ch: beauty

sn: 90

listtype: new

temp: 1

项目实现

1、创建爬虫项目和爬虫文件

scrapy startproject So
cd So
scrapy genspider so image.so.com

2、定义要爬取的数据结构(items.py)

img_link = scrapy.Field()

3、爬虫文件实现图片链接抓取

# -*- coding: utf-8 -*-
import scrapy
import json
from ..items import SoItem
​
class SoSpider(scrapy.Spider):
    name = 'so'
    allowed_domains = ['image.so.com']
​
    # 重写Spider类中的start_requests方法
    # 爬虫程序启动时执行此方法,不去找start_urls
    def start_requests(self):
        for page in range(5):
            url = 'http://image.so.com/zj?ch=beauty&sn={}&listtype=new&temp=1'.format(str(page*30))
            # 把url地址入队列
            yield scrapy.Request(
                url = url,
                callback = self.parse_img
            )
​
    def parse_img(self, response):
        html = json.loads(response.text)
​
        for img in html['list']:
            item = SoItem()
            # 图片链接
            item['img_link'] = img['qhimg_url']
​
            yield item

4、管道文件(pipelines.py)

from scrapy.pipelines.images import ImagesPipeline
import scrapy
​
class SoPipeline(ImagesPipeline):
    # 重写get_media_requests方法
    def get_media_requests(self, item, info):
        yield scrapy.Request(item['img_link'])

5、设置settings.py

IMAGES_STORE = '/home/tarena/images/'

6、创建run.py运行爬虫

scrapy shell的使用

基本使用

  1. scrapy shell URL地址
  2. request.headers :请求头(字典)
  3. reqeust.meta    :item数据传递,定义代理(字典)
  4. response.text    :字符串
  5. response.body    :bytes
  6. response.xpath('')

scrapy.Request()

  1. url
  2. callback
  3. headers
  4. meta :传递数据,定义代理
  5. dont_filter :是否忽略域组限制, 默认False检查域组限制allowed_domains['']

设置中间件(随机User-Agent)

少量User-Agent切换

方法一

# settings.py
USER_AGENT = ''
DEFAULT_REQUEST_HEADERS = {}

方法二

# spider
yield scrapy.Request(url,callback=函数名,headers={})

大量User-Agent切换(中间件)

middlewares.py设置中间件

1、获取User-Agent

# 方法1 :新建useragents.py,存放大量User-Agent,random模块随机切换

# 方法2 :安装fake_useragent模块(sudo pip3 install fack_useragent)

from fake_useragent import UserAgent

ua_obj = UserAgent()
ua = ua_obj.random

2、middlewares.py新建中间件类,拦截传给下载器的请求内容

class RandomUseragentMiddleware(object):
    def process_request(self,reuqest,spider):
        ua = UserAgent()
        request.headers['User-Agent'] = ua.random

3、settings.py添加此下载器中间件

DOWNLOADER_MIDDLEWARES = {
    'Baidu.middlewares.TestDownloaderMiddleware': 543,
    'Baidu.middlewares.RandomUaDownloaderMiddleware': 300,
    'Baidu.middlewares.TestRandomProxyMiddleware': 400,
}

设置中间件(随机代理)

import random
from fake_useragent import UserAgent


# 随机User-Agent下载器中间件
class RandomUaDownloaderMiddleware(object):
    def process_request(self, request, spider):
        # 给每一个拦截下来的请求包装随机User-Agent
        ua = UserAgent()
        useragent = ua.random
        # request.headers: 字典
        request.headers['User-Agent'] = useragent

        print('我是中间件:', useragent)  # 测试

proxy_list = ['http://1.1.1.1:1111', 'http://2.2.2.2:2222']

# 随机代理IP下载器中间件
class TestRandomProxyMiddleware(object):
    def process_request(self, request, spider):
        proxy = random.choice(proxy_list)  # 1. 随机选择并定义好代理
        request.meta['proxy'] = proxy  # 2. 如何包装
        print('我是中间件2:', proxy)  # 测试

    # 处理异常,一旦代理不能用,则返回请求再次执行下载器中间件,把请求扔回调度器
    def process_exception(self, request, exception, spider):
        return request

查看原文: scrapy爬虫框架

  • crazyduck
  • heavyelephant
  • smallbird
  • 浪沁