爬虫

爬虫

什么是爬虫?就是伪造浏览器,下载网页源代码,再从源代码获取需要的数据,一般分为两步: 1、伪造浏览器下载网页(requests模块) 2、对网页的内容进行结构化处理(BeautifulSoup模块)

requests模块

安装:pip install requests 下面是这个模块常用到的方法、属性
# 实例化对象,把所有的内容都封装到对象里,以下是get请求
response = requests.get(url="https://XXXXXXX")
# post请求,某些网站做了限制,需要headers中添加User-Agent、Host、Referer
response = requests.post(url="https://XXXXXXX",data={},headers={}, cookies={})
# 获取cookies
response.cookies.get_dict()
# 获取状态码
print(response.status_code)
# 获取文本内容,
print(response.text)
# 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk
# 设置编码
response.encoding = 'gbk'
print(response.text)
# 获取二进制格式的文本内容
print(response.content)

关于请求头中Content-Type

这个头主要有两种类型application/x-www-form-urlencoded;charset=UTF-8和application/json;charset=UTF-8 第一种主要是用于发送简单数据,如简单字典{'k1':'v1','k2':'v2}, 第二种主要是用于发送复杂数据,如复杂字典,值中又包含字典的,案例如下:
# django服务器端
def server(request):
    # request.POST只能处理简单请求,也就是请求头中Content-Type是application/x-www-form-urlencoded;charset=UTF-8
    # print(request.POST)
    # 要处理复杂请求,也就是请求头中Content-Type是application/json;charset=UTF-8
    # 需要从request.body中获取,再解码,然后再json.loads
    print(json.loads(request.body.decode('utf-8')))
    return HttpResponse('已接收')
# 客户端
import requests
import json

# 发送简单数据给服务器
# response = requests.post(
#     url=' http://127.0.0.1:8000/server/',
#     headers={
#         # 默认值,可不设置
#         'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
#     },
#     data={'name': 'Treelight', 'age': 18}
# )
# print(response.text)

# 发送复杂数据比如字典的值只能为列表、数字、字符串等,不可以用字典,如以下的data
# 这就请求头中的Content-Type用到application/json格式,同时需要用到json
# response = requests.post(
#     url=' http://127.0.0.1:8000/server/',
#     headers={
#         # 默认值
#         'Content-Type': 'application/json;charset=UTF-8',
#     },
#     data=json.dumps({
#         'disk': ['disk1', 'disk2'],
#         'cpu': {
#             1: 'cpu1',
#             2: 'cpu2',
#             3: 'cpu3',
#         }
#     })
# )
# print(response.text)

# 发送复杂数据的第二种形式
response = requests.post(
    url=' http://127.0.0.1:8000/server/',
    json=({
        'disk': ['disk1', 'disk2'],
        'cpu': {
            1: 'cpu1',
            2: 'cpu2',
            3: 'cpu3',
        }
    })
)
print(response.text)

requests参数

response = requests.request(
				method='get', # 请求方式
				url="", # 请求地址
				params = {}, # get形式传的参数  requests.get(url='http://www.cnblogs.com/wupeiqi',params={'id':1,'page':2})
				data={}, # 发送的请求体内容,
				json={}, # 发送的请求体内容,内部json.dumps({....})
				headers={},
				cookies={},
				proxies={}, # 代理、

				files = (), #上传文件
				auth=None, # 简单常用的验证规则
				timeout=(1,2), # 连接超时;响应超时
				allow_redirects=True, # 允许重定向
				stream=True, # 流式下载
				cert=(), # 证书
			)
import requests

session = requests.Session()

### 1、首先登陆任何页面,获取cookie
i1 = session.get(url="http://dig.chouti.com/help/service")

### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
i2 = session.post(
	url="http://dig.chouti.com/login",
	data={
		'phone': "8615131255089",
		'password': "xxxxxx",
		'oneMonth': ""
	}
)

i3 = session.post(
	url="http://dig.chouti.com/link/vote?linksId=8589623",
)
print(i3.text)

更多请参考https://www.cnblogs.com/wupeiqi/articles/5354900.html
请求体:FormData
Request
用户凭证:响应体 cookie中

BeautifulSoup模块

安装:pip install BeautifulSoup4 这个模块是对下载的html或xml内容进行结构化处理的 常用的属性、方法如下:
# 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构
soup = BeautifulSoup(response.text, 'html.parser')
# 找到id为i1的标签
tag1 = soup.select('#i1')
# 属性的增删改
del tag1.attrs['name']
tag1.attrs['name'] = 'username'
# 如果没有此属性则会增加
tag1.attrs['result'] = 'Good'
# 找孩子
tag1.children
# 找子子孙孙
tag1.descendsants
# 删除标签里的所有内容,但不包括自己
tag1.clear
# 全部删除,包括自己
tag1.decompose()
# 全部删除,包括自己,而且有返回值
ret = tag1.extra()
# 转换成字符串
tag1.decode()
# 转换成字符串,但不包括自己
tag1.decode_contents()
# 转换成字节
tag1.encode()
# 转换成字节,但不包括自己
tag1.encode_contents()
# 找第一个标签,文本为Beckham
tag2 = soup.find(name='div', attrs={'class':'form-control','id':'username'}, recursive=True, text='Beckham')
# 和上面效果一样的,但注意class需要有下划线,建议使用上面的
tag2 = soup.find(name='div', class_='form-control',id='username', recursive=True, text='Beckham')
# find_all:找到第一个div标签
tag3 = soup.find_all(name='div', limit=1)
# find_all要找的属性值可以是列表,以下是找到div或a标签
tag3 = soup.find_all(name=['div', 'a'])
# 找到类为form-control或myform的标签
tag3 = soup.find_all(class=['form-control', 'myform'])
# 也可为正则
reg = re.compile('form-.*')
tag3 = soup.find_all(class=reg)
# 也可为函数,找到同时有class和id属性的标签
def func(tag):
    return tag.has_attr('class') and tag.has_attr('id')
v = soup.find_all(name=func)
print(v)
# 标签是否有此属性
tag3.has_attr('class')
# 获取文本
tag3.get_text() 

更多请参考https://www.cnblogs.com/wupeiqi/articles/6283017.html

例一、爬取汽车之家的数据

import requests
from bs4 import BeautifulSoup

# 伪造浏览器,下载页面
# 实例化对象,把所有的内容都封装到对象里
response = requests.get(url="https://www.autohome.com.cn/news/")
# 获取状态码
# print(response.status_code)
# 获取文本内容,
# print(response.text)
# 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk
# 设置编码
response.encoding = 'gbk'
# print(response.text)
# 获取二进制格式的文本内容
# print(response.content)
# 下载页面完毕

# 结构化处理开始
# 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构
soup = BeautifulSoup(response.text, 'html.parser')
# 找到id是auto-channel-lazyload-article的div 标签,返回的是Tag对象
div = soup.find(name='div', id='auto-channel-lazyload-article')
# 从tag对象可继续找其下面的孩子,如找其li,find_all返回的是Tag对象列表
li_list = div.find_all(name='li')
for li in li_list:
    h3 = li.find(name='h3')
    a = li.find(name='a')
    p = li.find(name='p')
    img = li.find(name='img')
    if not h3:
        continue
    # 获取tag对象文本
    print(h3.text)
    # 获取属性
    print(a.get('href'))
    print(p.text)
    # 下载图片
    img_url = 'https:' + img.get('src')
    filename = img_url.rsplit('/', maxsplit=1)[1]
    img_res = requests.get(img_url)
    with open(filename, 'wb') as f:
        # 需要的是二进制格式
        f.write(img_res.content)
    # 下载图片结构
    print('-------------------------------------------------')
**------------恢复内容开始------------**

爬虫

什么是爬虫?就是伪造浏览器,下载网页源代码,再从源代码获取需要的数据,一般分为两步: 1、伪造浏览器下载网页(requests模块) 2、对网页的内容进行结构化处理(BeautifulSoup模块)

requests模块

安装:pip install requests 下面是这个模块常用到的方法、属性
# 实例化对象,把所有的内容都封装到对象里,以下是get请求
response = requests.get(url="https://XXXXXXX")
# post请求,某些网站做了限制,需要headers中添加User-Agent、Host、Referer
response = requests.post(url="https://XXXXXXX",data={},headers={}, cookies={})
# 获取cookies
response.cookies.get_dict()
# 获取状态码
print(response.status_code)
# 获取文本内容,
print(response.text)
# 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk
# 设置编码
response.encoding = 'gbk'
print(response.text)
# 获取二进制格式的文本内容
print(response.content)

关于请求头中Content-Type

这个头主要有两种类型application/x-www-form-urlencoded;charset=UTF-8和application/json;charset=UTF-8 第一种主要是用于发送简单数据,如简单字典{'k1':'v1','k2':'v2}, 第二种主要是用于发送复杂数据,如复杂字典,值中又包含字典的,案例如下:
# django服务器端
def server(request):
    # request.POST只能处理简单请求,也就是请求头中Content-Type是application/x-www-form-urlencoded;charset=UTF-8
    # print(request.POST)
    # 要处理复杂请求,也就是请求头中Content-Type是application/json;charset=UTF-8
    # 需要从request.body中获取,再解码,然后再json.loads
    print(json.loads(request.body.decode('utf-8')))
    return HttpResponse('已接收')
# 客户端
import requests
import json

# 发送简单数据给服务器
# response = requests.post(
#     url=' http://127.0.0.1:8000/server/',
#     headers={
#         # 默认值,可不设置
#         'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
#     },
#     data={'name': 'Treelight', 'age': 18}
# )
# print(response.text)

# 发送复杂数据比如字典的值只能为列表、数字、字符串等,不可以用字典,如以下的data
# 这就请求头中的Content-Type用到application/json格式,同时需要用到json
# response = requests.post(
#     url=' http://127.0.0.1:8000/server/',
#     headers={
#         # 默认值
#         'Content-Type': 'application/json;charset=UTF-8',
#     },
#     data=json.dumps({
#         'disk': ['disk1', 'disk2'],
#         'cpu': {
#             1: 'cpu1',
#             2: 'cpu2',
#             3: 'cpu3',
#         }
#     })
# )
# print(response.text)

# 发送复杂数据的第二种形式
response = requests.post(
    url=' http://127.0.0.1:8000/server/',
    json=({
        'disk': ['disk1', 'disk2'],
        'cpu': {
            1: 'cpu1',
            2: 'cpu2',
            3: 'cpu3',
        }
    })
)
print(response.text)

requests参数

response = requests.request(
				method='get', # 请求方式
				url="", # 请求地址
				params = {}, # get形式传的参数  requests.get(url='http://www.cnblogs.com/wupeiqi',params={'id':1,'page':2})
				data={}, # 发送的请求体内容,
				json={}, # 发送的请求体内容,内部json.dumps({....})
				headers={},
				cookies={},
				proxies={}, # 代理、

				files = (), #上传文件
				auth=None, # 简单常用的验证规则
				timeout=(1,2), # 连接超时;响应超时
				allow_redirects=True, # 允许重定向
				stream=True, # 流式下载
				cert=(), # 证书
			)
import requests

session = requests.Session()

### 1、首先登陆任何页面,获取cookie
i1 = session.get(url="http://dig.chouti.com/help/service")

### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
i2 = session.post(
	url="http://dig.chouti.com/login",
	data={
		'phone': "8615131255089",
		'password': "xxxxxx",
		'oneMonth': ""
	}
)

i3 = session.post(
	url="http://dig.chouti.com/link/vote?linksId=8589623",
)
print(i3.text)

更多请参考https://www.cnblogs.com/wupeiqi/articles/5354900.html
请求体:FormData
Request
用户凭证:响应体 cookie中

BeautifulSoup模块

安装:pip install BeautifulSoup4 这个模块是对下载的html或xml内容进行结构化处理的 常用的属性、方法如下:
# 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构
soup = BeautifulSoup(response.text, 'html.parser')
# 找到id为i1的标签
tag1 = soup.select('#i1')
# 属性的增删改
del tag1.attrs['name']
tag1.attrs['name'] = 'username'
# 如果没有此属性则会增加
tag1.attrs['result'] = 'Good'
# 找孩子
tag1.children
# 找子子孙孙
tag1.descendsants
# 删除标签里的所有内容,但不包括自己
tag1.clear
# 全部删除,包括自己
tag1.decompose()
# 全部删除,包括自己,而且有返回值
ret = tag1.extra()
# 转换成字符串
tag1.decode()
# 转换成字符串,但不包括自己
tag1.decode_contents()
# 转换成字节
tag1.encode()
# 转换成字节,但不包括自己
tag1.encode_contents()
# 找第一个标签,文本为Beckham
tag2 = soup.find(name='div', attrs={'class':'form-control','id':'username'}, recursive=True, text='Beckham')
# 和上面效果一样的,但注意class需要有下划线,建议使用上面的
tag2 = soup.find(name='div', class_='form-control',id='username', recursive=True, text='Beckham')
# find_all:找到第一个div标签
tag3 = soup.find_all(name='div', limit=1)
# find_all要找的属性值可以是列表,以下是找到div或a标签
tag3 = soup.find_all(name=['div', 'a'])
# 找到类为form-control或myform的标签
tag3 = soup.find_all(class=['form-control', 'myform'])
# 也可为正则
reg = re.compile('form-.*')
tag3 = soup.find_all(class=reg)
# 也可为函数,找到同时有class和id属性的标签
def func(tag):
    return tag.has_attr('class') and tag.has_attr('id')
v = soup.find_all(name=func)
print(v)
# 标签是否有此属性
tag3.has_attr('class')
# 获取文本
tag3.get_text() 

更多请参考https://www.cnblogs.com/wupeiqi/articles/6283017.html

例一、爬取汽车之家的数据

import requests
from bs4 import BeautifulSoup

# 伪造浏览器,下载页面
# 实例化对象,把所有的内容都封装到对象里
response = requests.get(url="https://www.autohome.com.cn/news/")
# 获取状态码
# print(response.status_code)
# 获取文本内容,
# print(response.text)
# 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk
# 设置编码
response.encoding = 'gbk'
# print(response.text)
# 获取二进制格式的文本内容
# print(response.content)
# 下载页面完毕

# 结构化处理开始
# 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构
soup = BeautifulSoup(response.text, 'html.parser')
# 找到id是auto-channel-lazyload-article的div 标签,返回的是Tag对象
div = soup.find(name='div', id='auto-channel-lazyload-article')
# 从tag对象可继续找其下面的孩子,如找其li,find_all返回的是Tag对象列表
li_list = div.find_all(name='li')
for li in li_list:
    h3 = li.find(name='h3')
    a = li.find(name='a')
    p = li.find(name='p')
    img = li.find(name='img')
    if not h3:
        continue
    # 获取tag对象文本
    print(h3.text)
    # 获取属性
    print(a.get('href'))
    print(p.text)
    # 下载图片
    img_url = 'https:' + img.get('src')
    filename = img_url.rsplit('/', maxsplit=1)[1]
    img_res = requests.get(img_url)
    with open(filename, 'wb') as f:
        # 需要的是二进制格式
        f.write(img_res.content)
    # 下载图片结构
    print('-------------------------------------------------')

Scrapy框架

安装

本人在pycharm中按以下步骤安装暂时没问题。由于要依赖twisted和wheel,: 步骤一、安装pywin32 步骤二、安装wheel 步骤三、安装twisted 步骤四、安装scrapy 下一次尝试一下直接安装scrapy

初次使用

使用上感觉上和Django差不多,都要创建项目、创建爬虫、编写内容、启动爬虫 创建项目:scrapy startproject 项目名称 以下操作必须要cd到项目目录文件夹下操作 创建爬虫:scrapy genspider 爬虫名称 域名 编写内容:在“项目名称\spider”中对应的爬虫名称文件编写 启动爬虫:scrapy crawl 爬虫名称 --nolog(没有日记)

scrapy爬虫流程

图片来源于https://www.cnblogs.com/wupeiqi/articles/6229292.html

定义起始url处理函数

运行scrapy crawl chouti --nolog,会找到name为chouti的类。然后根据起始url下载网页,下载完成后,会根据此类来定义回调函数处理下载的数据。定义起始urls回调函数的有两种方式
# 定义爬虫方式之一、不推荐,不灵活,因为如果有多个起始url,就会交给同一个parse函数处理
class ChoutiSpider(scrapy.Spider):
    name = 'chouti'
    allowed_domains = ['chouti.com']
    start_urls = ['https://dig.chouti.com/']

    def parse(self, response):
        print(response.text)
from scrapy.http import Request

# 定义爬虫处理方式二,推荐,不同的起始url可交给不同的函数处理
class ChoutiSpider(scrapy.Spider):
    name = 'chouti'
    allowed_domains = ['chouti.com']

    def start_requests(self):
        # 定义处理url下载完的处理函数,相当于加入图中的任务队列
        yield Request(url='https://dig.chouti.com', callback=self.parse_chouti)
        yield Request(url='https://www.baidu.com', callback=self.parse_baidu)
        # 相当于以下操作,两都均可
        # return [Request(url='https://dig.chouti.com', callback=self.parse_chouti),
        #         Request(url='https://www.baidu.com', callback=self.parse_baidu)]

    def parse_chouti(self, response):
        print('处理chouti', response)

    def parse_baidu(self, response):
        print('处理baidu', response)

选择器

在scrapy框架中,下载网页会自动完成,但要从网页中抽取数据,还是需要自己手动完成。可使用BeautifulSoup模块,可使用scrapy自带的选择器,这里就介绍一下选择器,先送上例子:
    def parse_chouti(self, response):
        """获取抽屉中所有的标题和其对应url"""
        # response封装了选择器可选择对象
        # 以以下为例,如果属性class是模糊匹配,则需要[contains(@class, "值")]
        # 属性精确匹配,则[@class="link-item"]
        # //是从根找,.//是从当前为根找子子孙孙,/只找孩子
        # 从最根部开始找子孙中class中含有link-item属性的div
        item_list = response.selector.xpath('//div[contains(@class, "link-item")]')
        for item in item_list:
            # 在选择条件后加上/text()获取文本对象,get方法是把提取第一个值
            # 相对get方法,也有get_list
            # 以当前标签作为根,在子孙中查找class中含有link-title的标签
            title = item.xpath('.//a[contains(@class, "link-title")]/text()').get()
            # 在选择条件后加/@属性获取属性值
            url = item.xpath('.//a[contains(@class, "link-title")]/@href').get()

知识点:
一、实例化选择器
方式一、response.selector.xpath('选择条件')
方式二、
from scrapy import Selector
tag = Selector(body=body).xpaht('选择条件')
二、搜索起点
//:从最根的目录查找子孙
.//:从当前标签开始找子孙
/:找儿子
三、属性可模糊匹配或精确匹配
1、模糊匹配,则需要[contains(@class, "值")]
2、精确匹配,则[@class="link-item"]
四、查找文本或属性选择器
文本:'选择条件/text()',返回文本选择器
属性:'选择条件/@href',返回href属性选择器
五、提取文本或属性
以上只是找到选择器,但没返回字符串,可通过get、getl_list取
1、get:以字符串形式返回第一个的文本或属性
2、get_list:返回的是列表
六、选择条件中使用正则表达式
格式://标签名[re:test(@属性, "正则表达式")]
如//div[re:test(@class, "^item-\d&")]

Pipelines

有一个pipelines.py文件,此文件里定义了一个类,有什么作用呢?这个文件定义了一个接口,提供了一些函数以供爬虫使用的前、中、后使用,比如: open_spider:在爬虫前调用此函数 close_spider:在爬虫执行完毕调用此函数 process_item:在爬虫回调函数中如果使用yield返回一个item对象,则执行此函数 也提供了以下方法: __init__:这个就不多说了 from_crawler:最先执行的方法,必须要返回一个对象(基本上不用),类的静态方法,需要加上@classmethod 以上方法执行的顺序 from_crawler--->__init__--->open_spider-->process_item-->close_spider 使用示例如下:
class DBPipeline(object):
    def process_item(self, item, spider):
        """回调函数执行完yield后执行此方法"""
        return item


class FilePipeline(object):
    def open_spider(self, spider):
        """在执行爬虫前执行的方法"""
        self.f = open('log.txt', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        """回调函数执行完yield后执行此方法"""
        self.f.write(item['title'] + '\n')
        self.f.flush()
        return item

    def close_spider(self, spider):
        """在爬虫执行完毕的方法"""
        self.f.close()

不过使用这些函数,还需要以下步骤:
一、在settings.py中注册,如下

# 值越小就优先执行
ITEM_PIPELINES = {
   's1.pipelines.DBPipeline': 300,
   's1.pipelines.FilePipeline': 400,
}

二、在items.py中定义item类

import scrapy


class S1Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    url = scrapy.Field()
    title = scrapy.Field()

三、在回调函数中使用yield,返回一个item对象

yield S1Item(title=title, url=url)

关于process_item函数的传递

由于pipelines中可注册多个类,也就是说会有多个process_item执行,它们的执行顺序是按照settings中设置的值执行,越小的越优先,而且首先执行的会传递item给下一个process_item,如下图 ![](https://img2018.cnblogs.com/blog/1503064/202002/1503064-20200211153437070-853001352.png)

如果不想传递,则可以这样做

from scrapy.exceptions import DropItem  # 导入模块


class DBPipeline(object):
    def process_item(self, item, spider):
        """回调函数执行完yield后执行此方法"""
        # 不传递item了
        return DropItem()

settings.py

一、pipelines配置
# 值越小就优先执行
ITEM_PIPELINES = {
   's1.pipelines.DBPipeline': 300,
   's1.pipelines.FilePipeline': 400,
}

二、递归层级限制:DEPTH_LIMIT = 2

三、过滤规则设置

DUPEFILTER_CLASS = 's1.dupefilter.RepeatUrl'

四、下载中间件设置

DOWNLOADER_MIDDLEWARES = {
   's1.middlewares.S1DownloaderMiddleware': 543,
}

五、是否遵循网站规则

ROBOTSTXT_OBEY = True

自定义过滤规则

比如说有一些网页已经爬过了,就不想再爬,可以通过过滤来。是怎么样过滤的呢?其实是把任务放到调度器前,它会经过过滤规则过滤一下看看是否符合规则的。例子如下:
class RepeatUrl:
    def __init__(self):
        self.visited_url = set()

    @classmethod
    def from_settings(cls, settings):
        """
        初始化时,调用
        :param settings: 
        :return: 
        """
        return cls()

    def request_seen(self, request):
        """
        检测当前请求是否已经被访问过
        :param request: 
        :return: True表示已经访问过;False表示未访问过
        """
        if request.url in self.visited_url:
            return True
        self.visited_url.add(request.url)
        return False

    def open(self):
        """
        开始爬去请求时,调用
        :return: 
        """
        print('open replication')

    def close(self, reason):
        """
        结束爬虫爬取时,调用
        :param reason: 
        :return: 
        """
        print('close replication')

    def log(self, request, spider):
        """
        记录日志
        :param request: 
        :param spider: 
        :return: 
        """
        print('repeat', request.url)

settings.py中

DUPEFILTER_CLASS = 's1.dupefilter.RepeatUrl'

来源https://www.cnblogs.com/wupeiqi/articles/6229292.html

下载中间件

scrapy提供了多个中间件,比较常用的是下载中间件,要使用中间件,要设置settings.py
DOWNLOADER_MIDDLEWARES = {
   's1.middlewares.S1DownloaderMiddleware': 543,
}

一般有以下的作用,前两个比较常用
1、设置请求头,返回None
2、设置代理,返回None
3、返回Response,流程如下:

4、返回Request,由于会添加到调试器,会不断在中间件和调度器来回流程如下:

5、丢弃请求
from scrapy.exceptions import IgnoreRequest
raise IgnoreRequest
所有示例请看以下例子:

中间件
# -*- coding: utf-8 -*-

# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals


class S1SpiderMiddleware(object):
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the spider middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        # Called for each response that goes through the spider
        # middleware and into the spider.

        # Should return None or raise an exception.
        return None

    def process_spider_output(self, response, result, spider):
        # Called with the results returned from the Spider, after
        # it has processed the response.

        # Must return an iterable of Request, dict or Item objects.
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
        # Called when a spider or process_spider_input() method
        # (from other spider middleware) raises an exception.

        # Should return either None or an iterable of Request, dict
        # or Item objects.
        pass

    def process_start_requests(self, start_requests, spider):
        # Called with the start requests of the spider, and works
        # similarly to the process_spider_output() method, except
        # that it doesn’t have a response associated.

        # Must return only requests (not items).
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)


class S1DownloaderMiddleware(object):
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        # 1、请求头处理,所有的请求都会加上
        request.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
        # 2、添加代理
        # 3、返回响应
        # from scrapy.http import Response
        # return Response(url='www.abc.com', body=b'request failed')
        # 4、 返回request,这样会不断地在下载中间件和调试器来回
        # from scrapy.http import Request
        # return Request(url='dig.chouti.com')
        # 5、丢弃请求
        # from scrapy.exceptions import IgnoreRequest
        # raise IgnoreRequest

    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

代理

实现方式有两种 一、内置 在start_request中加入以下语句
import os
os.environ['http_proxy'] = "http://root:woshiniba@192.168.11.11:9999/"
os.environ['https_proxy'] = "http://192.168.11.11:9999/"

二、下载中间件中实现

代理中间件
import random
import base64
import six

def to_bytes(text, encoding=None, errors='strict'):
    if isinstance(text, bytes):
	return text
    if not isinstance(text, six.string_types):
	raise TypeError('to_bytes must receive a unicode, str or bytes '
			'object, got %s' % type(text).__name__)
    if encoding is None:
	encoding = 'utf-8'
    return text.encode(encoding, errors)


class ProxyMiddleware(object):
    def process_request(self, request, spider):
	PROXIES = [
	    {'ip_port': '111.11.228.75:80', 'user_pass': ''},
	    {'ip_port': '120.198.243.22:80', 'user_pass': ''},
	    {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
	    {'ip_port': '101.71.27.120:80', 'user_pass': ''},
	    {'ip_port': '122.96.59.104:80', 'user_pass': ''},
	    {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
	]

	proxy = random.choice(PROXIES)
	if proxy['user_pass'] is not None:
	    request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
	    encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
	    request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)

	else:
	    request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])

扩展

自定义扩展时,利用信号在指定位置注册制定操作
自定义扩展
from scrapy import signals


class MyExtension(object):
    def __init__(self, value):
        self.value = value

    @classmethod
    def from_crawler(cls, crawler):
        val = crawler.settings.getint('MMMM')
        ext = cls(val)

        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)

        return ext

    def spider_opened(self, spider):
        print('open')

    def spider_closed(self, spider):
        print('close')
在settings.py中修改以下 #EXTENSIONS = { # 'scrapy.extensions.telnet.TelnetConsole': None,

自定制命令

在spiders同级创建任意目录,如:commands 在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)
自定制命令
from scrapy.commands import ScrapyCommand
    from scrapy.utils.project import get_project_settings


    class Command(ScrapyCommand):

        requires_project = True

        def syntax(self):
            return '[options]'

        def short_desc(self):
            return 'Runs all of the spiders'

        def run(self, args, opts):
            spider_list = self.crawler_process.spiders.list()
            for name in spider_list:
                self.crawler_process.crawl(name, **opts.__dict__)
            self.crawler_process.start()
在settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称' 在项目目录执行命令:scrapy crawlall
posted on 2020-02-07 01:00  Treelight  阅读(255)  评论(0编辑  收藏  举报