爬虫工作量由小到大的思维转变---<第四章 Scrapy不可迈的坎>

2023-12-13 06:38:43

前言:

? ? 行吧,咱们聊聊。你看,现在大家都在讲这个异步、多线程,挺火的。然后就有人开始拿来跟Scrapy比,说得好像Scrapy已经过时了似的。其实不是那么回事儿,你要是只想快速搭个接口什么的,那确实,用不着Scrapy。可要是想搞个大动作,像分布式这种大架构,那Scrapy简直就是救星,能省你不少事儿。

? ? 就好比组装电脑,有些哥们儿就是喜欢自己选配件,一手搭起来,那感觉确实爽。但Scrapy啊,它就像是那个成熟的电脑品牌,一直更新迭代,背后还有一大帮人支持。你买个成套的就完事了,啥都有了,多省心!

接着,得聊一聊,Scrapy这异步是怎么回事。它用的框架叫Twisted,老牌框架了,可不是现在流行的asyncio。asyncio那是后来Python 3.4引入的。用了Twisted的Scrapy,做大规模并发请求,效率那叫一个快。再加上它弄得那些下载器中间件、爬虫中间件、管道啥的,搞开发用起来就是顺手。

分布式嘛,Scrapy用的是Scrapy-Redis,串起来也挺轻松。让你的爬虫能在多台机器上干活,效率那叫一个杠,数据处理能力直接upup。这就是为啥如果你要搞点大动静,我肯定推Scrapy,靠谱!

但如果你想体验一下aiohttp在Scrapy里的感觉,那可得自己动手了。就是得弄个适配器之类的整合进去。比如写个中间件,用aiohttp代替Scrapy原生的下载器来发HTTP请求。这么干可能能用到aiohttp的一些独门秘技,但要是碰上问题,那得自己一个个查,Scrapy那边的,aiohttp这边的,都得弄。

所以呢,直白点说,咱直接用Scrapy就挺好。不麻烦,效率又高,特别是团队项目,简直完美。Scrapy的文档超全,社区也挺活跃,啥问题一查基本都能解决。

当然了,如果你就喜欢折腾,想亲手搞一搞,也行啊。技术圈子里自己动手那点乐趣儿是真实的。但是啊,别忘了,时间就是金钱,能用成熟的框架就别自己搞了,早点儿出成果才是王道。大项目上手了你就知道,这省力的意义有多大了。


正文:

那么怎么进行转换呢?

对标案例,可以看我之前写的:

CSDN

步骤:

根据提供的`IpPoolPipeline`,将Scrapy框架中的其他部分配合起来以实现完整的代理IP爬取和验证功能,以下是可能需要配合的几个主要部分:

1. Items:

在`items.py`中定义一个`ProxyItem`类,例子如下:

```python
# myproject/items.py
import scrapy

class ProxyItem(scrapy.Item):
? ? ip = scrapy.Field()
? ? port = scrapy.Field()


?

2. Spiders:


在蜘蛛(`spider`)文件中,需要在解析函数中产出`ProxyItem`,代码可能类似如下:

# myproject/spiders/ippool_spider.py
import scrapy
from myproject.items import ProxyItem

class IppoolSpider(scrapy.Spider):
? ? name = 'ippool'
? ? allowed_domains = ['example.com'] ?# 这里填写你将要爬取代理信息的网站域名
? ? start_urls = ['http://www.example.com/proxies']

? ? def parse(self, response):
? ? ? ? # 网页解析逻辑...
? ? ? ? for row in response.css('table tr'):
? ? ? ? ? ? item = ProxyItem()
? ? ? ? ? ? item['ip'] = row.xpath('./td[1]/text()').get()
? ? ? ? ? ? item['port'] = row.xpath('./td[2]/text()').get()
? ? ? ? ? ? yield item

3. Middleware:


如果需要在爬虫请求时使用代理,还可以定义一个中间件`middlewares.py`来为每个请求设置代理:

# myproject/middlewares.py
import random

class ProxyMiddleware(object):
? ? def process_request(self, request, spider):
? ? ? ? request.meta['proxy'] = 'http://some_proxy_server:port'
? ? ? ? # 或者使用随机代理:
? ? ? ? # request.meta['proxy'] = random.choice(PROXY_POOL)

4. Settings:


在`settings.py`中启用刚定义的`Item Pipeline`和(如果有的话)中间件:

# myproject/settings.py

ITEM_PIPELINES = {
? ? 'myproject.pipelines.IpPoolPipeline': 300,
? ? # ...
}

DOWNLOADER_MIDDLEWARES = {
? ? 'myproject.middlewares.ProxyMiddleware': 400,}
确保`settings.py`中的PIPELINES部分填写了正确的路径。这告诉Scrapy希望使用的`Item Pipeline`是`IpPoolPipeline`。

5. 运行Scrapy:
最后,可以运行Scrapy爬虫

提点:

在pipelines.py文件的地方,因为url抓取啥的没什么可讲,就那么点东西;但是关于ip验证和传入redis就是一个不好操作的地方,在哪里操作,怎么操作? 源代码放在这里:
import scrapy
from scrapy.exceptions import DropItem
from scrapy.utils.project import get_project_settings
import redis

class IpPoolPipeline:
    def __init__(self, crawler):
        self.crawler = crawler
        self.settings = get_project_settings()
        self.redis_conn = redis.StrictRedis(
            host=self.settings.get('REDIS_HOST'),
            port=self.settings.get('REDIS_PORT'),
            db=self.settings.get('REDIS_DB')
        )

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_item(self, item, spider):
        # 发起一个通过代理发起请求,以验证代理是否有效
        proxy = f"http://{item['ip']}:{item['port']}"
        meta = {'proxy': proxy, 'item': item, 'dont_retry': True, 'download_timeout': 10}
        return scrapy.Request(url="http://httpbin.org/ip", callback=self.validate_ip, meta=meta, dont_filter=True, errback=self.ignore_failure)

    def validate_ip(self, response):
        item = response.meta['item']
        
        # 如果代理可用,并且代理的IP出现在了我们的响应中,说明这个代理是有效的
        proxy_ip = item['ip']
        if proxy_ip in response.text:
            # 验证成功后将item存入Redis
            self.store_in_redis(item)
        else:
            # 如果验证不通过,直接抛出DropItem异常,item不会被进一步处理
            raise DropItem(f"无效的代理被忽略: {item['ip']}:{item['port']}")

    def ignore_failure(self, failure):
        item = failure.request.meta['item']
        # 只打印消息而不做进一步处理
        print(f"代理验证失败:{item['ip']}:{item['port']}")

    def store_in_redis(self, item):
        # 使用集合sadd来存储有效的IP,避免重复
        self.redis_conn.sadd('valid_ip', f"{item['ip']}:{item['port']}")

文章来源:https://blog.csdn.net/m0_56758840/article/details/134956312
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。