- 爬虫:通过编写程序员模拟浏览器上网,然后让其去互联网上爬取/获取数据的过程。
- 分类(使用场景):
- 通用爬虫:爬取一整张页面数据。“抓取系统”
- 聚焦爬虫:爬取的就是页面中指定的内容
- 增量式爬虫:用来检测网站数据更新的情况。只爬取网站最新更新的数据。
- 协议:客户端和服务器端进行数据交互的某种形式
- 请求头信息:
- User-Agent:请求载体的身份标识
- Connection:close
- 响应头信息:
- Content-Type:
- https:安全
- 加密方式:
- 对称秘钥加密:
- 非对称秘钥加密:
- 证书秘钥加密:
反爬机制: 反反爬:
1.robots.txt协议 忽略此协议 2.UA测验 在请求参数中添加headers(模拟网站发请求) 3.验证码 云打码、超级鹰、打码兔 4.cookie 用session发送请求 5、检测ip 找个代理(透明(能发现你的ip)、匿名(可以发现代理服务器的ip,不能发现你的ip)、高匿(既不能发现你的也不能发现代理的ip)) 6、动态参数 动态获取 7、动态加载 selenium滑动加载
- requests模块:网络请求的一个模块。 - 环境的安装:pip install requests - reqeusts模块的作用: - 模拟浏览器发请求。 - requests模块的编码流程: - 1.指定url - 2.发起请求 - 3.获取响应数据 - 4.持久化存储
案例1:爬取sogou首页文本内容
#导入模块 import requests #指定url url = 'https://www.sogou.com/' #发起请求:get方法的返回值就是一个响应对象 response = requests.get(url=url) #获取响应数据:text属性返回的是字符串形式的响应数据 page_text = response.text #step_4:持久化存储 with open('./sogou.html','w',encoding='utf-8') as fp: fp.write(page_text)
案例2:爬取搜狗指定词条搜索后的页面数据
import requests url = 'https://www.sogou.com/web' #处理url请求的参数 wd = input('enter a word:') param = { 'query':wd } response = requests.get(url=url,params=param) #设置响应数据的编码 response.encoding = 'utf-8' page_text = response.text print(page_text) name = wd+'.html' with open(name,'w',encoding='utf-8') as fp: fp.write(page_text) print(name,'爬取成功!')
- 编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
- 如何实现数据解析
- 正则
- bs4
- xpath
- 数据解析的原理
- 实现标签定位
- 将标签中存储的文本内容或者相关的属性值进行提取
案例:如何爬取一张图片数据(方法一)
import requests url = 'https://pic.qiushibaike.com/system/pictures/12176/121763419/medium/GKAGFQXQTVNBYK37.jpg' headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } response = requests.get(url=url,headers=headers) #content二进制形式的图片数据 img_data = response.content with open('./qiutu.jpg','wb') as fp: fp.write(img_data)
案例:如何爬取一张图片数据(方法二)
import requests from urllib import request url = 'https://pic.qiushibaike.com/system/pictures/12176/121763419/medium/GKAGFQXQTVNBYK37.jpg' request.urlretrieve(url=url,filename='./qiubai.jpg')#filename存储文件路径
案例:爬取所有的糗图数据
import requests import os import re from urllib import request url = 'https://www.qiushibaike.com/pic/' headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } page_text = requests.get(url=url,headers=headers).text #创建一个文件夹用来存储下载好的所有的图片 if not os.path.exists('./qiutu'): #指定路径 os.mkdir('./qiutu') #进行数据解析(img标签) ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>' img_src_list = re.findall(ex,page_text,re.S) for src in img_src_list: src = 'https:'+src name = src.split('/')[-1] img_path = './qiutu/'+name request.urlretrieve(src,img_path) print(name,'下载成功!') print('下载完毕,共下载图片数量为:',len(img_src_list))
- 环境安装:
- pip install bs4
- pip install lxml
- bs4解析原理
- 实例化一个BeautifulSoup对象,必须把即将被解析的页面源码加载到该对象中
- 调用该对象中相关的属性或者方法进行标签的定位和内容的提取
- 如何实例化一个BeautifulSoup对象
- 本地加载:
- soup = BeautifulSoup(fp,'lxml')
- 网络加载:
- soup = BeautifulSoup(page_text,'lxml')
- soup.tagName:定位标签(只可以定位到第一次出现的标签),返回的永远是一个单数
- soup.find(tagName,attrName="value"):基于属性定位实现的标签定位。返回的永远是一个单数
- soup.find_all():返回的永远是一个列表
- 取文本:
- string:取得标签中直系的文本内容
- text/get_text():取得的是标签下面所有的文本内容
- 取属性:tag['attrName']
- select:使用选择器定位标签。返回的是列表
- 标签,类,id选择器:select('选择器')
- 层级选择器:
- 单层级:'.tang > ul > li
- 多层级:'.tang li
案例:爬取三国演义整片小说内容
import requests from bs4 import BeautifulSoup #获取页面源码数据 url = ' http://www.shicimingju.com/book/sanguoyanyi.html' headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } page_text = requests.get(url=url,headers=headers).text #实例化soup对象 soup = BeautifulSoup(page_text,'lxml') #解析出章节标题和详情页的链接 li_list = soup.select('.book-mulu > ul > li') fp = open('./sanguo.txt','w',encoding='utf-8') for li in li_list: title = li.a.string detail_url = 'http://www.shicimingju.com'+li.a['href'] #对详情页url发起请求,进行章节内容的解析提取 detail_page_text = requests.get(url=detail_url,headers=headers).text #将详情页中的章节内容解析提取 detail_soup = BeautifulSoup(detail_page_text,'lxml') div_tag = detail_soup.find('div',class_="chapter_content") content = div_tag.text fp.write(title+':'+content+'\n') print(title,'下载存储成功!') fp.close()
- 通用性比较强
- 环境的安装:pip install lxml
- 解析原理:
- 1.实例化一个etree对象,且将解析的页面源码加载到该对象中
- 2.使用该对象中的xpath方法结合着xpath表达式进行标签定位和数据解析提取
- etree对象的实例化:
- 本地加载:
tree = etree.parse('filePath')
- 网络加载:
tree = etree.HTML(page_text)
常用的xpath表达式:基于标签的层级实现定位. 返回的永远是一个列表
- /:从根标签开始实现层级定位
- //:从任意位置实现标签的定位
- 属性定位:tag[@attrName="attrValue"]
- 索引定位://div[@class="tang"]/ul/li[5] 索引值是从1开始
- 取文本:
- 取得直系文本内容:/text()
- 取得所有的文本内容://text()
- 取属性:/@attrName
案例:爬取58二手房的房源信息
from lxml import etree import requests headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } url = 'https://bj.58.com/shahe/ershoufang/?PGTID=0d30000c-0047-e8f5-0211-cc2e07b06314&ClickID=1' page_text = requests.get(url=url,headers=headers).text #数据解析(房屋的名称和价格) tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@class="house-list-wrap"]/li') all_data_list = [] for li in li_list: name = li.xpath('./div[2]/h2/a/text()')[0]# ./表示的就是li标签 detail_url = li.xpath('./div[2]/h2/a/@href')[0] if 'https:' not in detail_url: detail_url = 'https:'+detail_url price = li.xpath('./div[3]//text()') price = ''.join(price) # print(name,price,detail_url) #获取详情页的页面源码数据,提取解析出房屋概况 detail_page_text = requests.get(url=detail_url,headers=headers).text tree = etree.HTML(detail_page_text) desc = tree.xpath('//div[@id="generalSituation"]//text()') desc = ''.join(desc) dic = { 'name':name, 'price':price, 'desc':desc } all_data_list.append(dic) print(all_data_list,len(all_data_list)
案例:需求爬取当前页面全部的城市名称https://www.aqistudy.cn/historydata/
url = 'https://www.aqistudy.cn/historydata/' page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) #热门城市://div[@class="bottom"]/ul/li/a/text() #全部城市://div[@class="bottom"]/ul/div[2]/li/a/text() all_city_names = tree.xpath('//div[@class="bottom"]/ul/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()') print(all_city_names,len(all_city_names))
- 爬取基于某些用户的用户信息
- 使用流程:
- 注册
- 登陆:
- 普通用户:
- 查询剩余提分(充值)
- 开发者用户:
- 创建软件:我的软件-》添加新软件(ID,秘钥)
- 下载示例代码:开发文档-》点此下载:云打码接口DLL-》PythonHTTP示例下载
import http.client, mimetypes, urllib, json, time, requests ###################################################################### class YDMHttp: apiurl = 'http://api.yundama.com/api.php' username = '' password = '' appid = '' appkey = '' def __init__(self, username, password, appid, appkey): self.username = username self.password = password self.appid = str(appid) self.appkey = appkey def request(self, fields, files=[]): response = self.post_url(self.apiurl, fields, files) response = json.loads(response) return response def balance(self): data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey} response = self.request(data) if (response): if (response['ret'] and response['ret'] < 0): return response['ret'] else: return response['balance'] else: return -9001 def login(self): data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey} response = self.request(data) if (response): if (response['ret'] and response['ret'] < 0): return response['ret'] else: return response['uid'] else: return -9001 def upload(self, filename, codetype, timeout): data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)} file = {'file': filename} response = self.request(data, file) if (response): if (response['ret'] and response['ret'] < 0): return response['ret'] else: return response['cid'] else: return -9001 def result(self, cid): data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid)} response = self.request(data) return response and response['text'] or '' def decode(self, filename, codetype, timeout): cid = self.upload(filename, codetype, timeout) if (cid > 0): for i in range(0, timeout): result = self.result(cid) if (result != ''): return cid, result else: time.sleep(1) return -3003, '' else: return cid, '' def report(self, cid): data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid), 'flag': '0'} response = self.request(data) if (response): return response['ret'] else: return -9001 def post_url(self, url, fields, files=[]): for key in files: files[key] = open(files[key], 'rb'); res = requests.post(url, files=files, data=fields) return res.text ###################################################################### # 普通用户名 username = 'bobo328410948' # 密码 password = 'bobo328410948' # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得! appid = 6003 # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得! appkey = '1f4b564483ae5c907a1d34f8e2f2776c' # 图片文件 filename = 'getimage.jpg' # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html codetype = 1004 # 超时时间,秒 timeout = 30 # 检查 if (username == 'username'): print('请设置好相关参数再测试') else: # 初始化 yundama = YDMHttp(username, password, appid, appkey) # 登陆云打码 uid = yundama.login(); print('uid: %s' % uid) # 查询余额 balance = yundama.balance(); print('balance: %s' % balance) # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果 cid, result = yundama.decode(filename, codetype, timeout); print('cid: %s, result: %s' % (cid, result)) ###############################################
案例:人人网携带验证码的模拟登陆
import requests from lxml import etree from urllib import request # from CodeClass import YDMHttp #封装识别验证码图片的函数 def getCodeText(codeType,filePath): result = None # 普通用户名 username = 'bobo328410948' # 密码 password = 'bobo328410948' # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得! appid = 6003 # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得! appkey = '1f4b564483ae5c907a1d34f8e2f2776c' # 图片文件 filename = filePath # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html codetype = codeType # 超时时间,秒 timeout = 40 # 检查 if (username == 'username'): print('请设置好相关参数再测试') else: # 初始化 yundama = YDMHttp(username, password, appid, appkey) # 登陆云打码 uid = yundama.login(); print('uid: %s' % uid) # 查询余额 balance = yundama.balance(); print('balance: %s' % balance) # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果 cid, result = yundama.decode(filename, codetype, timeout); print('cid: %s, result: %s' % (cid, result)) return result url = 'http://www.renren.com/SysHome.do' headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } page_text = requests.get(url=url,headers=headers,proxies={'http':'116.228.233.90:8082'}).text #解析出验证码图片的地址 tree = etree.HTML(page_text) code_img_url = tree.xpath('//*[@id="verifyPic_login"]/@src')[0] request.urlretrieve(url=code_img_url,filename='./code.jpg') #使用打码平台识别验证码 code_text = getCodeText(2004,'./code.jpg') # print(code_text) #模拟登陆 login_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2019401113761' data = { "email": "www.zhangbowudi@qq.com", "icode": code_text, "origURL": "http://www.renren.com/home", "domain": "renren.com", "key_id": "1", "captcha_type": "web_login", "password": "17108719a8907d7a716631afc519561080a50ea31d92f64ae5f9ccd64b41e4b1", "rkey": "465733ef9b05bd790ec81e9347025808", "f": "http%3A%2F%2Fwww.renren.com%2F289676607", } #创建一个回话对象 session = requests.Session() #产生cookie response = session.post(url=login_url,headers=headers,data=data,proxies={'http':'119.180.135.210:8060'}) # print(response.status_code) # page_text = response.text # print(page_text) #该次请求发送必须携带cookie detail_url = 'http://www.renren.com/289676607/profile' detail_page_text = session.get(url=detail_url,headers
- 快代理
- 西祠代理
- goubanjia
- 匿名度:
- 透明:对方服务器知道你使用了代理ip也知道你的真实ip
- 匿名:知道你使用了代理ip但是不知道你的真实ip
- 高匿:什么都不知道
- 类型:
- http:只可以发起http请求
- https:只可以发起https的请求
案例:简单代理池应用
http_list = [ {'http':'60.190.250.120:8080'}, {'http':'60.190.250.120:8080'}, {'http':'60.190.250.120:8080'} ] https_list = [ {'https':'60.190.250.120:8080'}, {'https':'60.190.250.120:8080'}, {'https':'60.190.250.120:8080'} ] url = 'http://www.baidu.com/s?wd=ip' page_text = requests.get(url=url,headers=headers,proxies={'http':'60.190.250.120:8080'}).text with open('./ip.html','w',encoding='utf-8') as fp: fp.write(page_text) import random url = 'http://www.baidu.com/s?wd=ip' response = requests.get(url=url,headers=headers,proxies=) page_text = response.text if response.url.split(':')[0] == 'http' with open('./ip.html','w',encoding='utf-8') as fp: fp.write(page_text)
- cookie:服务器端记录客户端的相关状态
- 处理cookie的方式:
- 手动处理:不建议
- 自动处理:回话对象Session,该对象可以像requests模块一样进行网络请求的发送(get,post)。session进行的请求发送可以自动携带和处理cookie。
案例:基于cookie的案例分析:https://xueqiu.com/
import requests headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } #自动获取cookie,cookie就会自动存储到session中 session = requests.Session() session.get('https://xueqiu.com/',headers=headers) #捕获ajax数据包获取的url url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=-1&count=10&category=-1' #携带cookie进行的请求发送 dic_json = session.get(url=url,headers=headers).json() print(dic_json) #从响应数据中获取详情页的url # for dic in dic_json['list']: # # print(dic) # d = dic['data'] # detail_url = 'https://xueqiu.com'+d['target'] # print(detail_url
- 验证码处理:打码兔,云打码
- 模拟登陆:点击登陆按钮对应的post发起请求(参数的处理和携带)
- 爬取登陆后的先关的页面数据(模拟登陆后会产生cookie)
- 如何处理cookie:
- 手动:
- 自动:回话对象。
- 代理:
- 网站
- 匿名度:
- 类型:
- 代理应用在requests中
- get和post(参数):
- url
- headers
- data/prams
- proxies
- 多进程或者多线程(不建议)
- 线程池或者进程池(适当使用)
- 单线程+异步协程(推荐)
案例:实例化一个线程池提高爬虫效率
import requests import re from lxml import etree from multiprocessing.dummy import Pool import random headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } def request_video(url): return requests.get(url=url,headers=headers).content def saveVideo(data): name = str(random.randint(0,9999))+'.mp4' with open(name,'wb') as fp: fp.write(data) print(name,'下载存储成功!!!') url = 'https://www.pearvideo.com/category_1' page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@id="listvideoListUl"]/li') #实例化一个线程池对象 pool = Pool(4) video_url_list = [] #所有的视频连接 for li in li_list: detail_url = 'https://www.pearvideo.com/'+li.xpath('./div/a/@href')[0] detail_page_text = requests.get(url=detail_url,headers=headers).text ex = 'srcUrl="(.*?)",vdoUrl=' video_url = re.findall(ex,detail_page_text,re.S)[0] video_url_list.append(video_url) #异步的获取4个视频的二进制数据 video_data_list = pool.map(request_video,video_url_list) #进行视频的持久化存储 pool.map(saveVideo,video_data_list
- 概念:用来完成浏览器自动化相关的操作。可以通过代码的形式制定一些基于浏览器自动化的相关操作(行为动作),当代码执行后,浏览器就会自动触发先关的事件
- 环境安装:
- pip install selenium
- 下载对应浏览器的驱动程序
- 编码流程:
- 导包:from selenium import webdriver
- 实例化某一款浏览器对象
- 制定相关的行为动作
案例:执行js实现滚轮向下滑动
from selenium import webdriver from time import sleep bro = webdriver.Chrome(executable_path='./chromedriver.exe') bro.get('https://xueqiu.com/') sleep(5) #执行js实现滚轮向下滑动 js = 'window.scrollTo(0,document.body.scrollHeight)' #向下滑动一屏高度, bro.execute_script(js) sleep(2) bro.execute_script(js) sleep(2) bro.execute_script(js) sleep(2) bro.execute_script(js) sleep(2) a_tag = bro.find_element_by_xpath('//*[@id="app"]/div[3]/div/div[1]/div[2]/div[2]/a') a_tag.click() sleep(5) #获取当前浏览器页面数据(动态) print(bro.page_source) bro.qui
案例:PhantomJs是一款无可视化界面的浏览器(免安装)
from selenium import webdriver from time import sleep bro = webdriver.PhantomJS(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据\爬虫day03\phantomjs-2.1.1-windows\bin\phantomjs.exe') bro.get('https://xueqiu.com/') sleep(2) bro.save_screenshot('./1.png') #执行js实现滚轮向下滑动 js = 'window.scrollTo(0,document.body.scrollHeight)' bro.execute_script(js) sleep(2) bro.execute_script(js) sleep(2) bro.execute_script(js) sleep(2) bro.execute_script(js) sleep(2) bro.save_screenshot('./2.png') # a_tag = bro.find_element_by_xpath('//*[@id="app"]/div[3]/div/div[1]/div[2]/div[2]/a') # bro.save_screenshot('./2.png') # a_tag.click() sleep(2) #获取当前浏览器页面数据(动态) print(bro.page_source) bro.quit(
案例:谷歌无头浏览器
from selenium import webdriver from time import sleep from selenium.webdriver.chrome.options import Options # 创建一个参数对象,用来控制chrome以无界面模式打开 chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') bro = webdriver.Chrome(executable_path='./chromedriver.exe',options=chrome_options) bro.get('https://www.baidu.com') sleep(2) bro.save_screenshot('1.png') #标签定位 tag_input = bro.find_element_by_id('kw') tag_input.send_keys('人民币') sleep(2) btn = bro.find_element_by_id('su') btn.click() sleep(2) print(bro.page_source) bro.quit
案例:前进和后退
from selenium import webdriver from time import sleep bro = webdriver.Chrome(executable_path='./chromedriver.exe') bro.get('https://www.baidu.com') sleep(1) bro.get('http://www.goubanjia.com/') sleep(1) bro.get('https://www.taobao.com') sleep(1) bro.back() sleep(1) bro.forward() sleep(1) print(bro.page_source) bro.quit(
案例一:动作链
from selenium import webdriver from time import sleep from selenium.webdriver import ActionChains bro = webdriver.Chrome(executable_path='./chromedriver.exe') url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' bro.get(url=url) #如果定位的标签存在于iframe标签之中,则必须经过switch_to操作在进行标签定位 bro.switch_to.frame('iframeResult') source_tag = bro.find_element_by_id('draggable') #创建一个动作连的对象 action = ActionChains(bro) action.click_and_hold(source_tag) for i in range(4): #perform表示开始执行动作链 action.move_by_offset(20,0).perform() sleep(1) bro.quit()
案例二:动作链
from selenium import webdriver from time import sleep from selenium.webdriver import ChromeOptions from selenium.webdriver import ActionChains option = ChromeOptions() option.add_experimental_option('excludeSwitches', ['enable-automation']) bro = webdriver.Chrome(executable_path='./chromedriver.exe',options=option) url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' bro.get(url=url) #如果定位的标签存在于iframe标签之中,则必须经过switch_to操作在进行标签定位 bro.switch_to.frame('iframeResult') source_tag = bro.find_element_by_id('draggable') taget_tag = bro.find_element_by_id('droppable') #创建一个动作连的对象 action = ActionChains(bro) action.drag_and_drop(source_tag,taget_tag) action.perform() sleep(3) # bro.quit()
- event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。程序是按照设定的顺序从头执行到尾,运行的次数也是完全按照设定。当在编写异步程序时,必然其中有部分程序的运行耗时是比较久的,需要先让出当前程序的控制权,让其在背后运行,让另一部分的程序先运行起来。当背后运行的程序完成后,也需要及时通知主程序已经完成任务可以进行下一步操作,但这个过程所需的时间是不确定的,需要主程序不断的监听状态,一旦收到了任务完成的消息,就开始进行下一步。loop就是这个持续不断的监视器。
- coroutine:中文翻译叫协程,在 Python 中常指代为协程对象类型,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。
- task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。
- future:代表将来执行或还没有执行的任务,实际上和 task 没有本质区别。
- 另外我们还需要了解 async/await 关键字,它是从 Python 3.5 才出现的,专门用于定义协程。其中,async 定义一个协程,await 用来挂起阻塞方法的执行。
案例:基本使用
import asyncio #创建一个协程函数 async def hello(name): print('hello to :',name) #获取了一个协程对象 c = hello('bobo') #创建一个事件循环对象 loop = asyncio.get_event_loop() #将协程对象注册到事件循环中,然后启动事件循环对象 loop.run_until_complete(c)
案例:task的使用
import asyncio async def hello(name): print('hello to :',name) c = hello('bobo') loop = asyncio.get_event_loop() #就协程进行进一步的封装,封装到了task对象中 task = loop.create_task(c) print(task) loop.run_until_complete(task) print(task)
案例:future的使用
import asyncio async def hello(name): print('hello to :',name) c = hello('bobo') task = asyncio.ensure_future(c) loop.run_until_complete(task)
案例:绑定回调(task)
def callback(task): print('i am callback:',task.result()) import asyncio async def hello(name): print('hello to :',name) return name c = hello('bobo') task = asyncio.ensure_future(c) #给任务对象绑定一个回调函数 task.add_done_callback(callback) loop.run_until_complete(task)
案例:多任务异步协程(假)
import asyncio async def request(url): print('正在下载:',url) sleep(2) #非异步模块的代码:在此处如果存在非异步操作代码,则会彻底让asyncio失去异步的效果 print('下载成功:',url) urls = [ 'www.baidu.com', 'www.taobao.com', 'www.sogou.com' ] start = time.time() loop = asyncio.get_event_loop() tasks = [] #任务列表,放置多个任务对象 for url in urls: c = request(url) task = asyncio.ensure_future(c) tasks.append(task) #将多个任务对象对应的列表注册到事件循环中 loop.run_until_complete(asyncio.wait(tasks)) print('总耗时:',time.time()-start)
案例:多任务异步协程(真)
import asyncio async def request(url): print('正在下载:',url) # sleep(2) #非异步模块的代码:在此处如果存在非异步操作代码,则会彻底让asyncio失去异步的效果 await asyncio.sleep(2) print('下载成功:',url) urls = [ 'www.baidu.com', 'www.taobao.com', 'www.sogou.com' ] start = time.time() loop = asyncio.get_event_loop() tasks = [] #任务列表,放置多个任务对象 for url in urls: c = request(url) task = asyncio.ensure_future(c) tasks.append(task) #将多个任务对象对应的列表注册到事件循环中 loop.run_until_complete(asyncio.wait(tasks)) print('总耗时:',time.time()-start)
实例:多任务异步操作应用到爬虫中(假)
import requests async def get_page(url): print('正在下载:',url) #之所以没有实现异步操作,原因是因为requests模块是一个非异步的模块 response = requests.get(url=url) print('响应数据:',response.text) print('下载成功:',url) start = time.time() urls = [ 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom' ] tasks = [] loop = asyncio.get_event_loop() for url in urls: c = get_page(url) task = asyncio.ensure_future(c) tasks.append(task) loop.run_until_complete(asyncio.wait(tasks)) print('总耗时:',time.time()-start)
实例:多任务异步操作应用到爬虫中(真)
支持异步的网络请求的模块:aiohttp 环境安装:pip install aiohttp import aiohttp import asyncio async def get_page(url): async with aiohttp.ClientSession() as session: async with await session.get(url=url) as response: page_text = await response.text() #read() json() print(page_text) start = time.time() urls = [ 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom' ] tasks = [] loop = asyncio.get_event_loop() for url in urls: c = get_page(url) task = asyncio.ensure_future(c) tasks.append(task) loop.run_until_complete(asyncio.wait(tasks)) print('总耗时:',time.time()-start)
实例:如何实现数据解析---任务的绑定回调机制
import aiohttp import asyncio #回调函数:解析响应数据 def callback(task): print('this is callback()') #获取响应数据 page_text = task.result() print('在回调函数中,实现数据解析') async def get_page(url): async with aiohttp.ClientSession() as session: async with await session.get(url=url) as response: page_text = await response.text() #read() json() # print(page_text) return page_text start = time.time() urls = [ 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom', 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/tom' ] tasks = [] loop = asyncio.get_event_loop() for url in urls: c = get_page(url) task = asyncio.ensure_future(c) #给任务对象绑定回调函数用于解析响应数据 task.add_done_callback(callback) tasks.append(task) loop.run_until_complete(asyncio.wait(tasks)) print('总耗时:',time.time()-start)
- 概述什么是http协议
- 爬虫中常用头信息
- https中涉及到的三种加密方式
- requests模块的作用及编码流程
- requests如何进行参数封装,为什么要进行参数封装
- 简述目前接触到的反爬机制及其反反爬策略
- 什么是动态加载数据
- 爬虫的分类
- 通用爬虫:
- 聚焦爬虫:
- 增量式:
- 爬取图片的两种方式
- 使用requests
- urllib模块request中urlretrive
- 数据解析的基本原理
- 标签的定位
- 取文本或取属性
- xpath解析原理
- 实例化etree对象,且将源码加载到该对象中
- 使用xpath方法结合着xpath表达式进行标签定位和数据提取
- 属性定位[@attrName="value"]
- 索引定位:[1]
- / //
- 取文本: /text() //text()
- 取属性:/@attrName
- etree对象实例化的方式
- 本地加载:parse
- 网络加载:HTML
- bs4解析原理
- .tagName 单数
- find(属性定位) 单数 find('tagName',attrName="value")
- find_all 复数
- Beautiful对象实例化方式
- 面试题:如何爬取携带标签的指定页面内容
- 验证码识别
- 使用流程
- 开发者用户登陆:
- 创建一个软件
- 下载示例代码
- 模拟登录
- 实现流程(携带验证码):
- 对登陆页面进行请求,从请求到的页面源码中解析下载验证码图片
- 使用打码平台对验证码进行识别
- 基于登陆按钮发起一个post请求(处理参数)
- 意义or作用:获取cookie
- cookie处理
- 处理方式:
- 手动处理:不建议(通用性不强)
- 自动处理:回话对象。requests.Session()
- 代理操作
- 为什么使用代理
- 免费代理ip网站
- 匿名度
- 透明:
- 匿名:
- 高匿:
- 类型:
- http:
- https:
- requests如何应用代理
- proxies = {'http':'ip:port'}
- 代理池(列表):
- selenium
- 作用:可以让浏览器完成相关自动化的操作
- 和爬虫的关联:
- 模拟登陆
- 可以获取动态加载的页面数据
- 编码流程
- 导包
- 实例化浏览器对象(驱动)
- 制定相关自动化的行为动作
- 标签定位
- find系列的函数
- 节点交互
- click
- send_keys
- 执行js
- 获取页面源码数据
- page_source
- iframe包含标签定位:
- switch_to.frame(id)
- 规避selenium被检测
- 无头浏览器
- robots.txt
- UA检测
- 验证码
- cookie
- 检测ip
- 动态参数(token,key)
- 动态加载的数据