协程大批量爬取是要被封IP的,最优秀的方法就是在被封IP时候立马切换IP

一. 多协程爬虫遇到的难点

    之前本着一封IP就切换IP的原则做了个协程爬虫。但是操作并发运行的爬虫和单线程的爬虫的难度真的是云泥之别。因为是并发运行的爬虫,用的IP是同一个,被封的时候当然是全部IP一起封了。

    而执行操作的时候,又会每个协程换一次,这就会导致IP的浪费和爬虫运行的缓慢。

二. 解决方案

    并发问题,自然要用到协程间通讯,Event。具体思想就是,当一个协程被封IP了,在切换IP的方法中,只让一条协程运行,其他全部休眠,待到IP切换成功再唤醒其余的协程。


为了方便调试,做了个socket服务端和客户端来模拟。

服务端:

#-*- coding:utf-8 -*-
from gevent import monkey; monkey.patch_all()

import socket
import json
import gevent
import threading

s = socket.socket()
host = '127.0.0.1'
port = 9991
s.bind((host, port))
ban_ip = []
ips = []

def processSocket(c):
	msg = c.recv(1024)
	json_msg = json.loads(msg.decode('utf-8').replace('\'', '"'))

	if json_msg['url'] == 'break':
		print('接收到消息, 退出')
		c.send('已成功退出,谢谢!!'.encode('utf-8'))
		c.close()
		s.close()
	else:
		if json_msg['ip'] in ban_ip:
			print(json_msg['ip'])
			print('恶意IP,查封!!')
			c.send('304'.encode('utf-8'))
			c.close()
		else:
			ips.append(json_msg['ip'])
			if len(ips) == 5:
				an = set(ips)
				if len(list(an)) ==	1:
					ban_ip.append(an.pop())
					ips.clear()
					if len(ips) == 0:
						print('已经清空...')
			print('接收到的信息:%s' %(msg.decode('utf-8')))
			c.send('200'.encode('utf-8'))
			c.close()	

print('socket 服务器开启!!!')
s.listen(50)
while True:
	c, addr = s.accept()
	gevent.spawn(processSocket, c)

客户端:

#-*- coding:utf-8 -*-
from gevent.pool import Pool
from gevent.lock import BoundedSemaphore
from gevent.event import Event
from gevent import monkey; monkey.patch_all()
# from threading import Event
from time import sleep

import socket
import random
import gevent
import threading





class client(object):

	def __init__(self):
		self.host = '127.0.0.1'
		self.port = 9991
		self.ip = 1
		self.isChanging = False
		self.event = Event()
		self.l = [{'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'},
			 {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'},
			 {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'a'}, {'ip':self.ip, 'url':'break'}]	

	def sendMsg(self, msg):
		# sleep(random.random()*5)
		s = socket.socket()
		gevent.sleep(random.random()*5)
		s.connect((self.host, self.port))
		msg['ip'] = self.ip
		s.send(str(msg).encode('utf-8'))

		resp = s.recv(1024)
		if resp.decode('utf-8') == '200':
			print('连接通过!!')
		elif resp.decode('utf-8') == '304':
			if self.isChanging:
				print('IP切换中, 休眠...')
				# event.wait()
				self.event.wait()
				msg['ip']=self.ip
				self.sendMsg(msg)
			else:
				print('IP被ban..请切换')
				self.checkoutIP()
		else:
			print(resp.decode('utf-8'))


	def checkoutIP(self):
		self.isChanging = True
		self.event.clear()
		self.ip = self.ip+1

		print('IP切换中...')
		gevent.sleep(5)
		# Event.set()
		# sleep(random.random()*5)
		print('切换成:%s' %(self.ip))
		self.isChanging = False
		self.event.set()

	def multiprocess(self):
		# for i in l:
		# 	t = threading.Thread(target=sendMsg, args=(i,)) 
		# 	t.start()
		p = Pool(3)
		threads = [p.spawn(self.sendMsg, s) for s in self.l]
		gevent.joinall(threads)


if __name__ == "__main__":
	c = client()
	# c.sendMsg({"ip":3, "url":"break"})
	c.multiprocess()

运行实况:


  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个基于Python 3的多协程以及队列爬取时光网电视剧top100的示例代码: ```python import requests from bs4 import BeautifulSoup import asyncio import aiohttp import time from queue import Queue async def fetch(session, url): async with session.get(url) as response: return await response.text() async def parse(html): soup = BeautifulSoup(html, 'html.parser') for item in soup.select('div[class="mov_con"] ul li'): rank = item.find('span', class_='top_num').text name = item.find('span', class_='mov_title').text score = item.find('span', class_='total_score').text print(rank, name, score) async def worker(session, queue): while not queue.empty(): url = queue.get() html = await fetch(session, url) await parse(html) async def main(): urls = [f'http://www.mtime.com/top/tv/top100/index-{i+1}.html' for i in range(10)] queue = Queue() for url in urls: queue.put(url) async with aiohttp.ClientSession() as session: tasks = [asyncio.create_task(worker(session, queue)) for _ in range(10)] await asyncio.gather(*tasks) if __name__ == '__main__': start_time = time.time() asyncio.run(main()) end_time = time.time() print(f'Time used: {end_time - start_time} seconds') ``` 这段代码使用了Python的asyncio库和aiohttp库来实现多协程异步爬取网页,使用了Python的queue模块来实现任务队列。首先,我们定义了`fetch`函数来异步获取网页内容,其返回值为响应的文本内容。然后,我们定义了`parse`函数来解析网页内容,提取出电视剧的排名、名称和评分,并输出到控制台。接着,我们定义了`worker`函数来作为协程的工作函数,从任务队列中取出一个URL并异步地解析该URL对应的网页。最后,我们定义了`main`函数来创建任务队列,创建异步协程,以及启动异步任务。在`main`函数中,我们先创建了10个URL,然后将这些URL放入任务队列中。接着,我们使用`async with`语句创建一个异步会话,并使用`create_task`函数创建10个异步协程,每个协程都调用`worker`函数,从任务队列中取出一个URL,并异步地解析对应的网页。最后,我们使用`asyncio.gather`函数等待所有的异步协程执行完毕。在程序执行结束后,我们还输出了程序的执行时间。 需要注意的是,由于时光网在一定时间内会对IP进行限制,如果爬虫速度过快可能会被IP,因此我们在程序中设置了一个1秒钟的延时,以避免被禁。如果您需要更高的爬取速度,请自行调整代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值