? ? 行吧,咱们聊聊。你看,现在大家都在讲这个异步、多线程,挺火的。然后就有人开始拿来跟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的文档超全,社区也挺活跃,啥问题一查基本都能解决。
当然了,如果你就喜欢折腾,想亲手搞一搞,也行啊。技术圈子里自己动手那点乐趣儿是真实的。但是啊,别忘了,时间就是金钱,能用成熟的框架就别自己搞了,早点儿出成果才是王道。大项目上手了你就知道,这省力的意义有多大了。
那么怎么进行转换呢?
对标案例,可以看我之前写的:
根据提供的`IpPoolPipeline`,将Scrapy框架中的其他部分配合起来以实现完整的代理IP爬取和验证功能,以下是可能需要配合的几个主要部分:
在`items.py`中定义一个`ProxyItem`类,例子如下:
```python
# myproject/items.py
import scrapy
class ProxyItem(scrapy.Item):
? ? ip = scrapy.Field()
? ? port = scrapy.Field()
?
# 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
# 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)
# myproject/settings.py
ITEM_PIPELINES = {
? ? 'myproject.pipelines.IpPoolPipeline': 300,
? ? # ...
}
DOWNLOADER_MIDDLEWARES = {
? ? 'myproject.middlewares.ProxyMiddleware': 400,}
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']}")