Scrapy | 通过爬取豆瓣Top250电影信息来学习在中间件中应用随机请求头和代理ip

中间件的使用

1.应用scrapy中使用间件使用随机UA的方法

2.应用scrapy中使用代理ip的的方法

3.应用scrapy.与selenium配合使用

1.scrapyl中间件的分类和作用

1.1 scrapy中间件的分类

根据scrapyi运行流程中所在位置不同分为:

python 复制代码
1.下载中间件
2.爬虫中间件

1.2 scrapy中间的作用:预处理request和response对象

python 复制代码
1.对header以及cookie进行更换和处理
2.使用代理ip等
3.对请求进行定制化操作,

但在scrapy默认的情况下两种中间件都在middlewares.py一个文件中

爬虫中间件使用方法和下载中间件相同,且功能重复,通常使用下载中间件

2.下载中间件的使用方法:

接下来我们对腾讯招聘爬虫进行修改完善,通过下载中间件来学习如何使用中间件编写一个 Downloader Middlewares和我们编写一个pipeline一样,定义一个类,然后在setting中开启
1.在middlerware.py中定义中间件类

2.在中间件类中,重写处理清求或者响应的方法

3.在settings文件中开启中间件的使用

python 复制代码
Downloader Middlewares默认的方法:


process_request(self,request,spider):
	1.当每个requesti通过下载中间件时,该方法被调用。
	2.返回None值:没有return也是返回None,该request对象传递给下载器,或通过引擎传递给其他权重低的process,_request方法
	3.返回Response对象:不再请求,把response返回给引繁
	4.返回Request对象:把request对象通过引擎交给调度器,此时将不通过其他权重低的process_request方法


protess_response(self,request,response,spider):
	1.当下载器完成http请求,传递响应给引擎的时候调用
	2.返回Resposne:通过引擎交给爬虫处理或交给权重更低的其他下载中间件的process_response方法
	3.返回Request对象:通过引擎交给调取器继续请求,此时将不通过其他权重低的process_request方法

在settings.py中配置开启中间件,权重值越小越优先执行

3.定义实现随机User-Agent的下载中间件

3.1 实战:爬取豆瓣Top250电影信息

  • 网页分析

  • 代码

记得在settings.py设置User-Agent和ROBOTSTXT_OBEY,否则会报403错误

python 复制代码
# items.py
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class DoubanItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()
    info = scrapy.Field()
    score = scrapy.Field()
    desc = scrapy.Field()
    pass
python 复制代码
import scrapy
from douban.items import DoubanItem

class MovieSpider(scrapy.Spider):
    name = 'movie'
    allowed_domains = ['douban.com']
    start_urls = ['https://movie.douban.com/top250']

    def parse(self, response):
        node_list = response.xpath('//div[@class="info"]')
        # print(len(node_list))

        for node in node_list:
            item = DoubanItem()
            item['name'] = node.xpath('./div[1]/a/span[1]/text()').get()
            item['info'] = node.xpath('./div[@class="bd"]/p[1]/text()').extract_first().replace('\n','').replace(' ','')
            item['score'] = node.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()').extract_first()
            item['desc'] = node.xpath('./div[2]/p[2]/span/text()').extract_first()

            yield item


        nexturl = response.xpath('//span[@class="next"]/a/@href').get()   #没有 a  则到最后一页提取的是Nnoe

        if nexturl != None:
            url = response.urljoin(nexturl)
            yield scrapy.Request(
                url=url
            )
  • 结果

3.2 中间件使用实现随机User-Agent

如上,请求头是写死在settings.py中的,请求发送的多了 也是会出问题的

python 复制代码
USER_AGENT_List=[
    "Opera/12.0(Windows NT 5.2;U;en)Presto/22.9.168 Version/12.00",
    "Opera/12.0(Windows NT 5.1;U;en)Presto/22.9.168 Version/12.00",
    "Mozilla/5.0 (Windows NT 5.1) Gecko/20100101 Firefox/14.0 Opera/12.0",
    "Opera/9.80 (Windows NT 6.1; WOW64; U; pt) Presto/2.10.229 Version/11.62",
    "Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.10.229 Version/11.62",
    "Mozilla/5.0 (Microsoft Windows NT 6.2.9200.0); rv:22.0) Gecko/20130405 Firefox/22.0",
    "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1",
    "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1",
    "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:21.0.0) Gecko/20121011 Firefox/21.0.0",
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0",
    "Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))",
    "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; InfoPath.3; MS-RTC LM 8; .NET4.0C; .NET4.0E)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.112)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; Tablet PC 2.0; InfoPath.3; .NET4.0C; .NET4.0E)",
]
  • middlewares.py设置
python 复制代码
import random
from douban.settings import  USER_AGENT_List
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter

class RandomUserAgent(object):
	#处理请求头  设置随机请求头 
    def process_request(self,request,spider):
        # print(request.headers['User-Agent'])
        ua = random.choice(USER_AGENT_List)
        request.headers['User-Agent']=ua
python 复制代码
DOWNLOADER_MIDDLEWARES = {
   # 'douban.middlewares.DoubanDownloaderMiddleware': 543,
    'douban.middlewares.RandomUserAgent': 543, # RandomUserAgent 中间件名称

}
python 复制代码
def parse(self, response):
    print(response.request.headers['User-Agent'])  # 增加一行测试代码

    node_list = response.xpath('//div[@class="info"]')
    # print(len(node_list))

    for node in node_list:
        item = DoubanItem()
        item['name'] = node.xpath('./div[1]/a/span[1]/text()').get()
        item['info'] = node.xpath('./div[@class="bd"]/p[1]/text()').extract_first().replace('\n','').replace(' ','')
        item['score'] = node.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()').extract_first()
        item['desc'] = node.xpath('./div[2]/p[2]/span/text()').extract_first()

        yield item
python 复制代码
scrpay crawl douban
  • 结果

    仔细观察上述结果有10个请求头,因为Top250的豆瓣网页每页25个电影数据 一共10页数据 因此每个页面使用一个请求头

4. 代理ip的使用

4.1思路分析

1.代理添加的位置:request...meta中增加proxy字段

2.获取一个代理ip,赋值给request.meta['proxy']

  • 代理池中随机选择代理p
  • 代理ip的webapi发送请求获取一个代理ip

4.2 代码实现

python 复制代码
PROXY_LIST = [
    {"ip_port":"123.207.53.84:16816","user_passwd":"morganne_mode_gigqc229x0"}, # 付费的稳定
    {"ip_port": "202.101.213.63:15007"} ,# 免费
    {"ip_port": "218.87.205.96:23763"},# 免费
]
python 复制代码
class RandomProxy(object):
    # 免费代理
    def process_request(self,request,spider):
        proxy = random.choice(PROXY_LIST)
        request.headers['proxy'] = proxy
        print(proxy)
        
		# 付费的有账号密码
        if 'user_passwd' in proxy:
            # 对帐号密码进行编码:
            base_up = base64.b64encode(proxy['user_passwd'].encode())
            # encode() 方法用于将字符串转换为字节串(bytes)
            # base64.b64encode() 需要一个字节串作为输

            # 设置认证
            request.headers['Proxy-Authorization'] = 'Basic '+ base_up.decode()
            # 将这个字节串解码为一个普通字符串(str),使其可以作为HTTP头部值或其他需要字符串格式的场景使用
            # Basic :后面有一个空格  这个一定要有   用来切割  一个是认证方式 一个是账号密码

            # 设置代理
            request.meta['proxy'] = proxy['ip_port']
        else:
            # 设置代理
            request.meta['proxy'] = proxy['ip_port']
代码:base64.b64encode(auth.encode()).decode()

在Python中,base64.b64encode() 函数用于将字节串(bytes)编码为Base64格式的字符串。auth.encode() 是将字符串(str)转换为字节串(bytes),因为 base64.b64encode() 需要一个字节串作为输入。

以下是详细步骤和代码解释:

  1. 字符串转字节串

    • auth.encode():将字符串 auth 转换为字节串。在Python 3中,字符串是以Unicode形式存储的,而 encode() 方法将Unicode字符串转换为字节串。默认情况下,它使用UTF-8编码,但你也可以指定其他编码方式。
  2. 字节串编码为Base64

    • base64.b64encode():这个函数接受一个字节串作为输入,并返回一个新的字节串,该字节串是原始字节串的Base64编码。
  3. 字节串解码为字符串

    • .decode():Base64编码后的结果是一个字节串,.decode() 方法将这个字节串解码为一个普通字符串(str),使其可以作为HTTP头部值或其他需要字符串格式的场景使用。

我们使用 requests 库发送一个带有代理认证的HTTP GET请求。认证信息被编码为Base64,并设置在请求头中。这样,请求就可以通过需要认证的代理服务器了。

5. 在中间件中使用selenium - 未实现 了解即可

空气质量历史数据查询为例

获取爬取网页的url(包括一级网址、二级网址) >>> 依据二级网址获得城市指数 >>> 依据一级网址热门城市的城市名称、城市链接(根据城市链接【二级网址】获得城市的指数数据

5.1分析网页数据格式

  • 首页

  • 具体城市页

  • 具体城市具体月份

因为数据页不让调试 因此 此部分代码用做学习 并没有运行结果

python 复制代码
import scrapy

from AQI.items import AqiItem


class AirdataSpider(scrapy.Spider):
    name = 'airdata'
    allowed_domains = ['aqistudy.cn']
    start_urls = ['https://www.aqistudy.cn/historydata/']

	#  这个还是可以运行的
    def parse(self, response):
        # 获取城市名列表      内容很多  只用一个 阿坝州
        city_name_list = response.xpath('/html/body/div[3]/div/div[1]/div[2]/div[2]/ul/div[2]/li/a/text()').getall()[0]
        print('city_name_list : %s'%city_name_list )
        # 获取跳转具体城市url列表:   内容很多  只用一个  阿坝州
        link_list = response.xpath('/html/body/div[3]/div/div[1]/div[2]/div[2]/ul/div[2]/li/a/@href').extract()[0:1]
        print('link_list : %s'% link_list )

        # 遍历列表
        for name, link in zip(city_name_list, link_list):
            # 给城市名赋值
            item = AqiItem()
            item['city_name'] = name
            # 完整的link
            # link = self.base_url + str(link)
            link = response.urljoin(link)
            print('link : %s' % link)
            # 发起请求,获取月度信息
            yield scrapy.Request(url=link, meta={'api_item': item}, callback=self.parse_month)

	def parse_month(self, response):
		pass

    def parse_day(self, response):
		pass

5.2 selenium中间件的使用 ⭐

scrapy本身就相当于request,它不能处理渲染之后的数据,因此使用selenium来处理渲染后的数据 ,具体城市具体月份对应的网页数据是需要渲染的

python 复制代码
from selenium import webdriver
import scrapy
import time


# 通过中间件自定义 webdriver的下载器
class ChromeMiddlewares(object):
    def process_request(self, request, spider):
        # 网址
        url = request.url

        # 判断,如果首页,不需要自定义
        if url != 'https://www.aqistudy.cn/historydata/':
            # 发送请求
            driver = webdriver.Chrome()
            driver.get(url)

            # 注意添加延迟  由于是动态加载的
            time.sleep(2)

            # 获取数据
            data = driver.page_source

            # 关闭浏览器
            driver.close()

            # 构建自己的response对象,直接返回
            return scrapy.http.HtmlResponse(url=url, body=data, encoding='utf-8', request=request)
  • 开启中间件
相关推荐
Lspecialnx_2 小时前
文件解析漏洞中间件(iis和Apache)
网络安全·中间件
隔着天花板看星星2 小时前
Spark-Streaming集成Kafka
大数据·分布式·中间件·spark·kafka
漫无目的行走的月亮9 小时前
基于Python Scrapy的豆瓣Top250电影爬虫程序
爬虫·python·scrapy
大霞上仙21 小时前
selenium 在已打开浏览器上继续调试
python·selenium·测试工具
Autumn.h1 天前
文件解析漏洞
web安全·网络安全·中间件
武汉联从信息1 天前
Oracle 中间件 Webcenter Portal服务器环境搭建
中间件
小码哥说测试2 天前
Charles简单压力测试
自动化测试·软件测试·网络协议·selenium·接口测试·压力测试·postman
OkeyProxy2 天前
靜態IP與DHCP的區別和用法
静态ip·dhcp·代理ip·代理服务器·海外ip代理
OkeyProxy2 天前
怎麼在模擬器中實現換IP
网络协议·proxy模式·代理ip·模拟器·海外ip代理
钱钱钱端2 天前
软件测试工程师面试整理 —— 编程与自动化!
自动化测试·软件测试·selenium·测试工具·面试·职场和发展