我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取,提升爬取数据的效率。
安装一个scrapy-redis的组件。原生的scarapy是不可以实现分布式爬虫的,我们必须要让scrapy结合着scarapy-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)【推荐】
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
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield 3
yield 4
f1=func1()
for item in f1:
print(item)
在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))
在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())