爬虫学习笔记04-分布式与协程

爬虫学习笔记04-分布式与协程

分布式

我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取,提升爬取数据的效率。

如何实现分布式?

安装一个scrapy-redis的组件。原生的scarapy是不可以实现分布式爬虫的,我们必须要让scrapy结合着scarapy-redis组件一起实现分布式爬虫。

为什么原生的scrapy不可以实现分布式?

调度器不可以被分布机群共享
管道不可以被分布式机群共享

scrapy-redis组件作用

-可以给原生的scrapy框架提供可以被共享的管道和调度器

实现流程

创建一个工程
创建一个基于CrawlSpider的爬虫文件
修改当前的爬虫文件:
-导包:from scrapy_redis.spider import RedisCrawlSpider
-将start_urls和allowed_domains进行注释
-添加一个新属性:redis_key='sun’可以被共享的调度器队列的名称
-编写数据解析相关的操作
-将当前爬虫类的父类修改成RedisCrawlSpider
-修改配置文件settings:
-指定使用可以被共享的管道:

ITEM_PIPELINES={
		'scrapy_redis.pipelines.RedisPipeline':400
} 

​ -指定调度器

#增加了一个去重容器类的配置,作用使用Redis的set集合来储存请求的指纹数据,从而实现请求去重的持久化存储
DUPEFILTER_CLASS='scrapy_redis.dupefilter.RFPDupeFilter'
#使用scrapy-redis组件自己的调度器
SCHEDULER='scrapy_redis.scheduler.Scheduler'
#配置调度器是否要持久化,也就是当爬虫结束了,要不要清空Redis中请求队列和去重指纹的set。
SCHEDULER_PERSIST=True

-redis相关操作配置:
-配置redis的配置文件:
-linux或者mac:redis.conf
-windows:redis.windows.conf
-打开配置文件修改:
①将bind 127.0.0.0进行注释
②关闭保护模式:protected-mode yes改为no
-结合着配置文件启动redis服务
-redis-server 配置文件
-启动客户端:
-redis-cli
-执行工程:
-scrapy runspider xxx.py
-向调度器的队列放入一个起始的url:
-调度器的队列在redis的客户端中
-lpush xxx www.xxx.com
-爬取到的数据存储在了redis的proName:items这个数据结构中

实战代码

from selenium import webdriver
from time import sleep
from lxml import etree
#实例化一个浏览器对象(传入浏览器的驱动程序)
bro=webdriver.Chrome(executable_path='./chromedriver.exe')
#让浏览器发起一个指定url对应请求
bro.get("http://scxk.nmpa.gov.cn:81/xk/")
#page_sourse获取浏览器当前页面的页面源码数据
page_text=bro.page_source
#解析企业名称
tree=etree.HTML(page_text)
li_list=tree.xpath('//*[@id="gzlist"]/li')
for li in li_list:
    name=li.xpath('./dl/@title')[0]
    print(name)
sleep(5)
bro.quit()
from selenium import webdriver
bro=webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('https://www.taobao.com')
#标签定位
search_input=bro.find_element_by_id('q')
#标签交互
search_input.send_keys('Iphone')
#执行一组js程序
bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
sleep(2)
#点击搜索按钮
btn=bro.find_element_by_css_selector('.btn-search')
btn.click()
bro.get('https://www.baidu.com')
sleep(2)
#回退
bro.back()
sleep(2)
bro.forward()
sleep(5)
bro.quit()
#selenium
from selenium import webdriver
from time import sleep
#导入动作链对应的类
from selenium.webdriver import ActionChains
bro =webdriver.Chrome(executable_path='./chromedriver')
bro.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
#如果定位的标签是存在于iframe标签之中的则必须通过如下操作再进行标签定位
bro.switch_to.frame('iframeResult')#切换浏览器标签定位的作用域
div=bro.find_element_by_id('draggable')
#动作链
action=ActionChains(bro)
#点击长按指定的标签
action.click_and_hold(div)
for i in range(5):
    #perform()立即执行动作链操作
    #move by offset(x,y):x表示水平方向,y表示竖直方向
    action.move_by_offset(17,0).perform()
    sleep(0.3)
#释放动作链
action.release()
print(div)
bro.quit()

#需求:模拟登陆QQ空间
from selenium import webdriver
from time import sleep

bro=webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('https://qzone.qq.com/')
bro.switch_to_frame('login_frame')
a_tag=bro.find_element_by_id('switcher_plogin')
a_tag.click()
userName_tag=bro.find_element_by_id('u')
password_tag=bro.find_element_by_id('p')
sleep(1)
userName_tag.send_keys('1264532114')
sleep(1)
password_tag.send_keys('123456789')
btn=bro.find_element_by_id('login_button')
sleep(1)
btn.click()
sleep(3)
bro.quit()
#需求:实现selenium规避检测
from selenium import webdriver
from time import sleep
from selenium.webdriver import ChromeOptions  #实现规避检测
from selenium.webdriver.chrome.options import Options #实现无可视化界面
#实现无可视化界面的操作
chrome_options=Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
#实现规避检测
option=ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])

#如何实现让selenium规避被检测到的风险
bro=webdriver.Chrome(executable_path='./chromedriver.exe')
#无可视化界面(无头浏览器) phantomJS
bro.get('https://www.baidu.com')
print(bro.page_source)
sleep(2)
bro.quit()

协程

协程(coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。

实现协程的方式

-greenlet,早期模块
-yield关键字
-asyncio装饰器(py3.4)
-async、await关键字(py3.5)【推荐】

grenlet实现协程
环境安装

pip3 install greenlet

from greenlet import greenlet
def func1():
	print(1)  #第一步输出1
	gr2.switch()#切换到func2函数
	print(2)#输出2
	gr2.switch()#切换到func2函数,从上一次执行的位置继续向后执行
def func2():
	print(3)
	gr1.switch()
	print(4)
gr1=greenlet(func1)
gr2=grrenlet(func2)
gr1.switch()#去执行func1函数

#输出:1324
yield关键字
def func1():
	yield 1
	yield from func2()
	yield 2
def func2():
	yield 3
	yield 4
f1=func1()
for item in f1:
	print(item)
asyncio

在python3.4以及之后的版本可以使用。

import asyncio 
@asyncio.coroutine
def func1():
	print(1)
	yield from asyncio.sleep(2)#遇到IO耗时操作,自动化切换到tasks中的其他任务
	print(2)

@asyncio.coroutine
def func2():
	print(3)
	yield from asyncio.sleep(2) #遇到IO耗时操作,自动化切换到tasks的其他任务
	print(4)

tasks=[
	asyncio.ensure_future(func1()),
	asyncio.ensure_future(func2())
]
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
async&await关键字

在python3.5及以后的版本可以使用

import asyncio 
async def func1():
	print(1)
	await asyncio.sleep(2)
	print(2)
	
async def func2():
	print(3)
	await asyncio.sleep(2)
	print(4)
	
tasks=[
	asyncio.ensure_future(func1()),
	asyncio.ensure_future(func2())
]
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
协程的意义

在一个线程中如果遇到IO等待时间,线程不会傻傻等,利用空闲的时候再去干点其他事。
案例:去下载三张图片(网络IO)。

·普通方式(同步)

import requests
def download_image(url):
    print("开始下载",url)
    #发送网络请求
    response=requests.get(url)
    print('下载完成')
    #图片保存到本地文件
    file_name=url.rsplit('_')[-1]
    with open(file_name,mode='wb') as file_object:
        file_object.write(response.content)


if __name__=='__main__':
    url_list=[
        'https://www.baidu.com/img/dong_8f1d47bcb77d74a1e029d8cbb3b33854.gif',
    ]
    for item in url_list:
        download_image(item)

异步协程
#下载图片使用第三方模块aiohttp,请提前安装:
#pip3 install aiohttp
import aiohttp
import asyncio

async def fetch(session,url):
    print("发送请求",url)
    async  with session.get|(url,verify_ssl=False) as response:
        content=await  response.content.read()
        file_name=url.rsplit('_')[-1]
        with open(file_name,mode='wb') as file_object:
            file_object.write(content)
    print('下载完成',url)

async def main():
    async with aiohttp.ClientSession() as session:
        url_list=[
            'https://www.baidu.com/img/dong_8f1d47bcb77d74a1e029d8cbb3b33854.gif',
        ]
    tasks=[asyncio.create_task(fetch(session,url)) for url in url_list]
    await asyncio.wait(tasks)


if __name__=='__main__':
    asyncio.run(main())

你可能感兴趣的:(爬虫,python,爬虫,学习,笔记,算法,python)