爬虫 第一章 :爬虫基础简介 什么是爬虫:通过编写程序,模拟浏览器上网,然后使其去互联网上抓取数据的过程。
爬虫在使用场景中的分类:
通用爬虫:抓取系统重要组成部分。抓取的是一整张页面数据。
聚焦爬虫:是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。
增量式爬虫:检测网站中数据更新的情况。只抓取网站中最新更新出来的数据。
反爬机制:门户网站可以通过制定相应的策略或技术手段,防止爬虫程序进行网站数据的爬取。
反反爬虫策略:爬虫程序可以通过制定相应的策略或技术手段,破解门户网站中具有的反爬虫机制,从而获取门户网站的数据。
robots.txt协议:规定了网站中哪些数据允许爬取以及哪些数据不允许爬取。
http协议:HyperText Transfer Protocol,超文本传输协议。
http协议的特点(重点):
应用层协议。(最顶层也是和用户交互的层)
无连接:http协议每次发送请求都是独立的。http 1.1以后有一个头:connection:keep_alive。
无状态:http协议不记录状态,进而产生了两种记录http状态的技术:cookie和session。
常用请求头信息:
User-Agent:请求载体的身份标识。
Connection:请求完毕后,是断开连接还是保持连接。
cookie:请求的状态信息。
Referer:表示产生请求的网页来源于哪里。
accept:允许传入的文件类型。
常用响应头信息:
Content-Type:服务器响应给客户端的数据类型。
https协议:HTTPS (Hypertext Transfer Protocol over Secure Socket Layer)简单讲是http的安全版,在http下加入SSL层。
SSL(Secure Sockets Layer 安全套接层)主要用于Web的安全传输协议,在传输层对网络连接进行加密,保障在Internet上数据传输的安全。
数据加密方式:
第二章:requests模块基础 requests安装:pip install requests
python中基于网络请求的模块:
requests模块介绍:python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高。
作用:模拟浏览器给服务器端发送请求。
使用(requests模块的编码流程):
指定url(UA伪装、请求参数处理)
发送请求
获取响应数据
持久化存储
requests常用方法和属性:
response.encoding:获取页面响应数据的编码格式。(response.encoding = ‘utf-8’:设置编码格式)
response.status_code:响应状态码。
response.headers:响应头信息。
User-Agent检测:门户网站会检测对应请求的载体身份标识,如果检测到请求的载体身份标识不是基于某一款浏览器,则服务器端可能拒绝该次请求。
User-Agent伪装:让爬虫程序对应的请求载体伪装成某一刻浏览器。
1 2 3 headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' }
requests发送get请求:requests.get(url=url, params=param, headers=headers)
发送get请求:获取请求的网页HTML文本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import requestsif __name__ == '__main__' : url = 'https://www.sogou.com/web' kw = input ('enter a word:' ) param = { 'query' : kw } headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } resp = requests.get(url=url, params=param, headers=headers) print (resp.url) page_text = resp.text with open (kw + '.html' , 'w' , encoding='utf-8' ) as f: f.write(page_text)
requests发送post请求:requests.post(url=url, data=data, headers=headers)
发送post请求:爬取百度翻译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsimport jsonif __name__ == '__main__' : url = 'https://fanyi.baidu.com/sug' kw = input ("enter a word:" ) data = { 'kw' : kw } headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } resp = requests.post(url=url, data=data, headers=headers) baidu_json = resp.json() f = open ('./' +kw+".json" , 'w' , encoding='utf-8' ) json.dump(baidu_json, fp=f, ensure_ascii=False )
看网站发送请求的方式:看地址信息有没有改变再抓包
看网站响应的数据格式:Content-Type
txt/html或text/plain(字符串):txt属性
application/json(对象):json方法
图片/音频等(二进制):content属性
持久化存储json数据:json.dump(json数据, fp=文件, ensure_ascii=False)
综合练习:爬取药监总局详情数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import requestsif __name__ == '__main__' : url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList' page = input ("请输入爬取的页数:" ) data = { 'on' : 'true' , 'page' : page, 'pageSize' : '15' , 'productName' : '' , 'conditionType' : '1' , 'applyname' : '' , 'applysn' : '' } headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } resp = requests.post(url=url, data=data, headers=headers) json_ids = resp.json() id_list = [] for dic in json_ids['list' ]: id_list.append(dic['ID' ]) print (id_list) url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById' all_detail_data = [] for i in id_list: data = { 'id' : i } resp = requests.post(url=url, data=data, headers=headers) detail_json = resp.json() print (detail_json) all_detail_data.append(detail_json)
第三章:数据解析 聚焦爬虫:爬取页面中指定的页面内容。
编码流程:1、指定url 2、发起请求 3、获取响应数据 4、数据解析 5、持久化存储
数据解析分类:1、正则表达式 2、bs4 3、xpath
数据解析原理概念:在网页的标签之间或者标签对应的属性中解析局部的文本内容进行存储。
进行指定标签的定位。
提取标签中或标签对应的属性中的数据。
正则表达式数据解析 正则表达式数据解析步骤:
import re
re.findall(正则表达式, 文本数据, re.S):返回符合条件数据的列表
正则表达式练习:分页爬取图片数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import requestsimport reimport osif __name__ == '__main__' : if not os.path.exists('./imgLibs' ): os.mkdir('./imgLibs' ) img_url_list = [] headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } url = 'https://pic.netbian.com/4kdongman/index_%d.html' for pageNum in range (2 , 5 ): print ('正在爬取第' + str (pageNum) + '页图片' ) new_url = format (url % pageNum) resp = requests.get(url=new_url, headers=headers) resp.encoding = 'gbk' page_text = resp.text ex = '.*?.html" target="_blank"><img src="(.*?)" alt=".*?' img_url_list = re.findall(ex, page_text, re.S) for img_url in img_url_list: img_url = 'https://pic.netbian.com' + img_url img_data = requests.get(url=img_url, headers=headers).content img_name = img_url.split('/' )[-1 ] img_path = './imgLibs/' + img_name with open (img_path, 'wb' ) as f: f.write(img_data) print (img_name, '下载成功!!' )
bs4数据解析 bs4数据解析(css选择器):
实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
通过调用BeautifulSoup对象中相关的属性或方法进行标签定位和数据提取
环境安装:
pip install bs4
pip install lxml:解析器
实例化BeautifulSoup对象:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html文件数据, ‘lxml’)
BeautifulSoup对象中的方法和属性(css选择器):
soup.tagName/soup.find(‘tagName’):返回页面第一次出现的该标签
soup.find(‘tagName’,class_=’value’):返回页面中有class=’value’的该标签
soup.find_all(‘tagName’):返回页面中所有该标签的一个列表
soup.select(‘css选择器’):返回符合要求的所有标签的一个列表
获取标签之间的文本数据:
soup.tagName.text/get_text():返回标签之间的所有文本数据
soup.tagName.string:只可以返回该标签下面直系的文本内容
获取标签的属性值:
soup.tageName[‘attributeName’]:获取标签指定的属性值
bs4练习:爬取三国演义小说
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import requestsfrom bs4 import BeautifulSoupimport reif __name__ == '__main__' : url = 'https://www.shicimingju.com/book/sanguoyanyi.html' headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } resp = requests.get(url=url, headers=headers) resp.encoding = 'utf-8' page_text = resp.text soup = BeautifulSoup(page_text, 'lxml' ) a_list = soup.select('.book-mulu > ul > li > a' ) f = open ('三国演义.txt' , 'w' , encoding='utf-8' ) for a in a_list: title = a.string detail_url = a['href' ] detail_url = 'https://www.shicimingju.com' + detail_url resp = requests.get(url=detail_url, headers=headers) resp.encoding = 'utf-8' detail_page_text = resp.text detail_soup = BeautifulSoup(detail_page_text, 'lxml' ) content = detail_soup.find('div' , class_='chapter_content' ).text f.write(title+":" +content+"\n" ) print (title, "爬取成功" )
xpath数据解析 xpath解析:最常用且最便捷高效的一种解析方式。
实例化一个etree对象,并且将页面源码数据加载到该对象中。
调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
实例化BeautifulSoup对象:
from lxml import etree
本地html文档中的源码数据加载到etree对象中:
互联网上获取的源码数据加载到etree对象中:
调用xpath方法进行标签定位:
xpath(‘xpath表达式’):返回Element对象
重点:xpath表达式——用于定位
获取标签之间的文本:
xpath表达式/text():返回该标签下面直系的文本内容列表
xpath表达式//text():返回该标签之间的所有文本内容列表
获取标签的属性值:
xpath表达式/@attributeName:获取标签指定的属性值
xpath练习:爬取城市名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import requestsfrom lxml import etreeif __name__ == '__main__' : url = 'https://www.aqistudy.cn/historydata/' headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } hot_city_names = [] all_city_names = [] page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) hot_city_list = tree.xpath("//div[@class='hot']//ul/li" ) for li in hot_city_list: hot_city_name = li.xpath("./a/text()" ) hot_city_names.append(hot_city_name[0 ]) print (hot_city_names, len (hot_city_names)) city_initial_list = tree.xpath("//div[@class='all']/div[2]/ul" ) for ul in city_initial_list: city_name_list = ul.xpath("./div[2]/li" ) for li in city_name_list: city_name = li.xpath("./a/text()" ) all_city_names.append(city_name[0 ]) print (all_city_names, len (all_city_names))
xpath/bs4结合练习:音效爬取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import requestsfrom lxml import etreefrom bs4 import BeautifulSoupimport osif __name__ == '__main__' : if not os.path.exists('./musicFile' ): os.mkdir('./musicFile' ) url = 'https://sc.chinaz.com/yinxiao/' headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55' } page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) music_List = tree.xpath("//div[@id='AudioList']//div[@class='right-head']/a" ) for a in music_List[0 ], music_List[1 ]: music_url = a.xpath("./@href" )[0 ] music_url = 'https://sc.chinaz.com' + music_url music_page_text = requests.get(url=music_url, headers=headers).text soup = BeautifulSoup(music_page_text, 'lxml' ) music_name = soup.select('.play-box > div >h1' )[0 ].string music_name = music_name.encode('iso-8859-1' ).decode('utf-8' ) mp3_url = soup.select('#mp3box > div:nth-of-type(2) > a:nth-of-type(1)' )[0 ]['href' ] mp3_url = 'https:' +mp3_url mp3_download = requests.get(url=mp3_url, headers=headers).content music_path = './musicFile/' +music_name+".mp3" with open (music_path, 'wb' ) as f: f.write(mp3_download) print (music_name+'.mp3' , '下载成功!' )
第四章:验证码识别 门户网站反爬机制:使用验证码。使用验证码可以防止应用或者网站被恶意注册、攻击。
识别验证码操作:
人工肉眼识别。(不推荐)
第三方自动识别。
python验证码识别库
第三方超级鹰 第三方超级鹰使用流程:
使用超级鹰识别验证码:
请求验证码图片地址,进行本地下载
调用超级鹰接口对验证码图片进行识别
使用返回的验证码识别结果
超级鹰识别验证:古诗文网验证码识别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import requestsfrom hashlib import md5class Chaojiying_Client (object ): def __init__ (self, username, password, soft_id ): self.username = username password = password.encode('utf8' ) self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user' : self.username, 'pass2' : self.password, 'softid' : self.soft_id, } self.headers = { 'Connection' : 'Keep-Alive' , 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)' , } def PostPic (self, im, codetype ): """ im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html """ params = { 'codetype' : codetype, } params.update(self.base_params) files = {'userfile' : ('ccc.jpg' , im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php' , data=params, files=files, headers=self.headers) return r.json() def ReportError (self, im_id ): """ im_id:报错题目的图片ID """ params = { 'id' : im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php' , data=params, headers=self.headers) return r.json()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import requestsfrom fake_useragent import UserAgentimport osfrom pcstudy.验证码处理.超级鹰.chaojiying import Chaojiying_Clientif __name__ == '__main__' : if not os.path.exists('./codeImg' ): os.mkdir('./codeImg' ) url = 'https://so.gushiwen.cn/RandCode.ashx' headers = { 'User-Agent' : UserAgent().chrome } code_img = requests.get(url=url, headers=headers).content with open ('./codeImg/code.jpg' , 'wb' ) as f: f.write(code_img) chaojiying = Chaojiying_Client('lrw5243' , '1274604930.qq' , '931400' ) im = open ('./codeImg/code.jpg' , 'rb' ).read() print (chaojiying.PostPic(im, 1902 )['pic_str' ])
OCR技术验证码识别 OCR技术简介:光学字符识别(Optical Character Recognition, OCR)是指对文本资料的图像文件进行分析识别处理,获取文字及版面信息的过程。亦即将图像中的文字进行识别,并以文本的形式返回。
ddddocr库的使用:
ocr技术识别验证:学习通登录验证码识别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import requestsfrom fake_useragent import UserAgentimport osimport ddddocrif __name__ == '__main__' : if not os.path.exists('./codeImg' ): os.mkdir('./codeImg' ) url = 'https://passport2.chaoxing.com/num/code' headers = { 'User-Agent' : UserAgent().chrome } code_img = requests.get(url=url, headers=headers).content with open ('./codeImg/xxt.jpg' , 'wb' ) as f: f.write(code_img) ocr = ddddocr.DdddOcr() with open ('./codeImg/xxt.jpg' , 'rb' ) as f: img_bytes = f.read() res = ocr.classification(img_bytes) print (res)
第五章:requests模块高级 Cookie处理 cookie和session理解:
cookie是网站用来辨别用户身份,进行会话跟踪,存储在本地终端上的数据。
session在web中主要用来在服务器端存储特定用户对象会话所需要的信息。
cookie和session产生的原因:
http协议是一个无状态协议,在特定操作的时候,需要保存信息,进而产生了cookie和session。
cookie原理:
由服务器来产生,浏览器第一次请求,服务器发送给客户端进而保存。
浏览器继续访问时,就会在请求头的cookie字段上附带cookie信息,这样服务器就可以识别是谁在访问了。
cookie处理方式:
手动处理:通过抓包工具获取cookie值,将该值封装到headers中。(不建议)
自动处理:requests创建session会话对象,使用session对象发送请求。
创建session会话对象:session = requests.session()
使用session会话对象发送请求:session.post(url=url, data=data, headers=headers)
作用:使用session会话对象进行请求的发送,如果请求过程中产生了cookie,则该cookie会被自动存储,再次使用该session会话对象发送请求就会携带cookie信息。
防止登录请求重定向:浏览器调试 Network ,勾选Preserve log,可以Console 保留历史日志和网络请求
需求:模拟登录,爬取门户网站用户登录后的个人信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import requestsfrom fake_useragent import UserAgentif __name__ == '__main__' : session = requests.session() url = 'https://xsgz.hufe.edu.cn/website/login' data = { 'uname' : '202108210651' , 'pd_mm' : 'e11c0a39f9b24b9ebb2513c199efe684' } headers = { 'User-Agent' : UserAgent().chrome } resp = session.post(url=url, data=data, headers=headers) print (resp.json()) welcome_url = 'https://xsgz.hufe.edu.cn/wap/main/welcome' welcome_page_text = session.get(url=welcome_url, headers=headers).text with open ('./welcome.html' , 'w' , encoding='utf-8' ) as f: f.write(welcome_page_text)
设置代理 代理实际上是代理服务器(proxy server),它的功能是代理网络用户获取网络信息。
IP检测:当我们用同一个IP多次频繁访问服务器时,服务器会检测到该请求可能是爬虫操作,服务器会封禁该IP,因此就不能正常的响应页面的信息了。
代理的作用:
HTTP 代理服务器:主要用于访问网页, 一般有内容过滤和缓存功能, 端口一般为80 、8080 、3128 等。
根据代理的匿名程度分类:
透明代理:服务端上知道该次请求是代理服务器,也能追踪到真实的IP。
普通匿名代理: 服务端上知道该次请求是代理服务器, 但追踪不到真实的IP。
高度匿名代理:可以完整的将IP变成代理服务器的IP,对方追踪不到真实的IP。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import requestsfrom fake_useragent import UserAgentfrom lxml import etreeif __name__ == '__main__' : url = 'http://www.baidu.com/s?wd=ip&usm=3&rsv_idx=2&rsv_page=1' headers = { 'User-Agent' : UserAgent().chrome } proxies = { 'http' : 'http://127.0.0.1' , 'https' : 'https://127.0.0.1' } resp = requests.get(url=url, headers=headers, proxies=proxies) resp.encoding = 'utf-8' page_text = resp.text with open ('./ip.html' , 'w' , encoding='utf-8' ) as f: f.write(page_text)
第六章:高性能异步爬虫 目的:在爬虫中使用异步实现高性能的数据爬取操作。
使用线程池 异步爬虫方式:使用线程池Pool
默认单线程串行方式执行:用时8秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import timeurl_list = ['http://www.baidu.com' , 'http://www.baidu.com' , 'http://www.baidu.com' , 'http://www.baidu.com' ] def get_page (url ): print ("正在爬取:" + url) time.sleep(2 ) print ("爬取成功!" ) start_time = time.time() for url in url_list: get_page(url) end_time = time.time() print (end_time - start_time)
使用线程池方式执行:2秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import timefrom multiprocessing.dummy import Poolurl_list = ['http://www.baidu.com' , 'http://www.baidu.com' , 'http://www.baidu.com' , 'http://www.baidu.com' ] def get_page (url ): print ("正在爬取:" + url) time.sleep(2 ) print ("爬取成功!" ) start_time = time.time() pool = Pool(4 ) pool.map (get_page, url_list) end_time = time.time() pool.close() pool.join() print (end_time - start_time)
爬取梨视频 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 import timeimport requestsfrom fake_useragent import UserAgentfrom lxml import etreefrom selenium import webdriverfrom random import randomfrom multiprocessing.dummy import Poolimport osimport reclass PearVideo : def __init__ (self ): if not os.path.exists('./videoSrc' ): os.mkdir('./videoSrc' ) self.headers = { 'User-Agent' : UserAgent().chrome } options = webdriver.ChromeOptions() options.add_argument('--headless' ) self.driver = webdriver.Chrome(options=options) def get_page_source (self, num, pear_url ): self.driver.get(pear_url) flag = '加载更多' sum = 9 while flag == '加载更多' and sum < num: self.driver.find_element_by_id('listLoadMore' ).click() flag = self.driver.find_element_by_id('listLoadMore' ).get_attribute('innerHTML' ) sum += 12 time.sleep(1 ) page_text = self.driver.page_source self.driver.quit() tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@id="categoryList"]/li' )[:num] return li_list def get_video_url (self, li_list ): data = [] for li in li_list: href = li.xpath('./div/a/@href' )[0 ] cont_id = href.split('_' )[-1 ] video_name = li.xpath('./div/a/div[2]/text()' )[0 ] + '.mp4' content_url = 'https://www.pearvideo.com/videoStatus.jsp' params = { 'contId' : cont_id, 'mrd' : random() } content_headers = { 'User-Agent' : UserAgent().chrome, 'Referer' : 'https://www.pearvideo.com/' + href } content_json = requests.get(url=content_url, params=params, headers=content_headers).json() download_url = content_json['videoInfo' ]['videos' ]['srcUrl' ] download_url = re.sub('/[0-9]+-' , '/cont-{}-' .format (cont_id), download_url) name_url = { 'name' : video_name, 'url' : download_url } data.append(name_url) return data def download_video (self, data ): print (data['name' ], '正在下载.............' ) video_data = requests.get(url=data['url' ], headers=self.headers).content video_path = './videoSrc/' + data['name' ] with open (video_path, 'wb' ) as f: f.write(video_data) print (data['name' ], '下载成功!!' ) if __name__ == '__main__' : pear_url = 'https://www.pearvideo.com/category_59' num = int (input ("输入要爬取的视频数量:" )) pear = PearVideo() li_list = pear.get_page_source(num, pear_url) data = pear.get_video_url(li_list) pool = Pool(10 ) pool.map (pear.download_video, data) pool.close() pool.join()
协程异步爬虫 第六章:selenium加持 selenium模块和爬虫之间的关联:
便捷的获取网站中动态加载的数据
driver.page_source:获取页面源码,包括JS动态加载的数据。
便捷实现模拟登录
处理简单滑块验证码:模拟登录12306
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from selenium import webdriverfrom time import sleepfrom selenium.webdriver import ActionChainsoptions = webdriver.ChromeOptions() options.add_experimental_option('excludeSwitches' , ['enable-automation' ]) options.add_argument("--disable-blink-features=AutomationControlled" ) options.add_argument('start-maximized' ) options.add_experimental_option('detach' , True ) driver = webdriver.Chrome(options=options) driver.get('https://kyfw.12306.cn/otn/resources/login.html' ) driver.find_element_by_id('J-userName' ).send_keys('17674737693' ) driver.find_element_by_id('J-password' ).send_keys('1274604930qq' ) driver.find_element_by_id('J-login' ).click() sleep(1 ) slip_block = driver.find_element_by_id('nc_1_n1z' ) action = ActionChains(driver) action.click_and_hold(slip_block).move_by_offset(300 , 0 ).perform() sleep(1 ) driver.find_element_by_xpath('//a[@class="btn btn-primary ok"]' ).click() print (driver.page_source)sleep(5 ) driver.quit()
处理图片验证码:模拟登录学习通
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from selenium import webdriverfrom time import sleepimport ddddocroptions = webdriver.ChromeOptions() options.add_experimental_option('excludeSwitches' , ['enable-automation' ]) options.add_argument("--disable-blink-features=AutomationControlled" ) options.add_argument('start-maximized' ) driver = webdriver.Chrome(options=options) driver.get('https://passport2.chaoxing.com/login' ) title = driver.title while title == '用户登录' : driver.find_element_by_id('unameId' ).send_keys('19373140107' ) driver.find_element_by_id('passwordId' ).send_keys('1274604930.qq' ) code = driver.find_element_by_id('numVerCode' ) code.screenshot('./code.png' ) ocr = ddddocr.DdddOcr() with open ('./code.png' , 'rb' ) as f: img_bytes = f.read() res = ocr.classification(img_bytes) print (res) driver.find_element_by_id('numcode' ).send_keys(res) driver.find_element_by_xpath('//input[@type="button"]' ).click() driver.refresh() title = driver.title sleep(2 ) driver.quit()
第七章:scrapy框架 scrapy介绍:scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
安装:pip install scrapy
可能报错,需要安装scrapy依赖库twisted
scrapy框架基本使用 scrapy框架的使用:
创建爬虫项目:scrapy startproject 项目名
创建爬虫文件:
在项目的spiders文件下创建
scrapy genspider 爬虫文件名 爬取的网址url
运行爬虫代码:
scrapy crawl 爬虫的名字
需要在项目的settings.py文件注释君子协议(ROBOTSTXT_OBEY = True)
项目结构:
初始爬虫文件:创建百度文件
1 2 3 4 5 6 7 8 9 10 11 12 13 import scrapyclass BaiduSpider (scrapy.Spider): name = 'baidu' allowed_domains = ['www.baidu.com' ] start_urls = ['http://www.baidu.com/' ] def parse (self, response ): pass
scrapy常用属性方法:
scrapy架构组成:
scrapy工作原理:
scrapy案例应用:爬取汽车之家信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import scrapyclass CarSpider (scrapy.Spider): name = 'car' allowed_domains = ['car.autohome.com.cn' ] start_urls = ['http://car.autohome.com.cn/price/brand-15.html' ] def parse (self, response ): name_list = response.xpath('//*[@class="list-cont-main"]/div/a/text()' ) price_list = response.xpath('//div[@class="main-lever-right"]/div/span/span/text()' ) for i in range (len (name_list)): name = name_list[i].extract() price = price_list[i].extract() print (name, price)
scrapy shell scrapy shell:Scrapy终端,是一个交互终端,供您在未启动spider的情况下尝试及调试您的爬取代码。
ipython使用:自动补全,高亮输出,及其他特性。
scrapy shell应用:scrapy shell 网址url
response对象:
response.body:二进制数据
response.text:页面源码数据
response.json:处理json数据
response.url:请求的url
response.status:响应状态码
response的解析:
response.xpath():xpath定位解析,返回一个selector列表对象
response.css():css属性定位解析,,返回一个selector列表对象
获取内容:response.css(‘#su::text’).extract_first()
获取属性:response.css(‘#su::attr(“value”)’).extract_first()
yield yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回yield后面(右边)的值。重点是:下一次迭代 时,从上一次迭代遇到的yield后面的代码(下一行)开始执行。
带有 yield 的函数不再是一个普通函数,而是一个生成器generator,可用于迭代。
分页爬取 案例:爬取当当图书网
一、cmd:创建爬虫项目
二、item.py:确定items
1 2 3 4 5 6 7 8 9 import scrapyclass DangdangItem (scrapy.Item): title = scrapy.Field() img = scrapy.Field() price = scrapy.Field()
三、dang.py:爬取信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import scrapyfrom dangdang.items import DangdangItemclass DangSpider (scrapy.Spider): name = 'dang' allowed_domains = ['category.dangdang.com' ] start_urls = ['http://category.dangdang.com/cp01.01.02.00.00.00.html' ] base_url = 'http://category.dangdang.com/pg' page = 1 def parse (self, response ): li_list = response.xpath('//ul[@id="component_59"]/li' ) for li in li_list: title = li.xpath('./a/@title' ).extract_first() img = li.xpath('./a/img/@data-original' ).extract_first() if img: img = img else : img = li.xpath('./a/img/@src' ).extract_first() price = li.xpath('./p[3]/span[1]/text()' ).extract_first() print (title, img, price) book = DangdangItem(title=title, img=img, price=price) yield book if self.page < 100 : self.page += 1 url = self.base_url + str (self.page) + '-cp01.01.02.00.00.00.html' yield scrapy.Request(url=url, callback=self.parse)
四、pipelines.py:管道下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from itemadapter import ItemAdapterclass DangdangPipeline : def open_spider (self, spider ): self.fp = open ('./books.json' , 'w' , encoding='utf-8' ) def process_item (self, item, spider ): self.fp.write(str (item)) return item def close_spider (self, spider ): self.fp.close() import urllib.requestclass DangdangDownloadPipeline : def open_spider (self, spider ): if not os.path.exists('./imgSrc' ): os.mkdir('./imgSrc' ) def process_item (self, item, spider ): url = 'http:' + item.get('img' ) img_path = './imgSrc/' + item.get('title' ) + '.jpg' urllib.request.urlretrieve(url=url, filename=img_path) return item
五、settings.py:配置信息
1 2 3 4 5 6 7 8 ROBOTSTXT_OBEY = False ITEM_PIPELINES = { 'dangdang.pipelines.DangdangPipeline' : 300 , 'dangdang.pipelines.DangdangDownloadPipeline' : 301 }
CrawlSpider CrawlSpider简介:
CrawlSpider继承自scrapy.Spider
CrawlSpider可以定义规则,再解析html内容的时候,可以根据链接规则提取出指定的链接,然后再向这些链接发送请求。
如果有需要跟进链接的需求,意思就是爬取了网页之后,需要提取链接再次爬取,使用CrawlSpider是非常合适的。
链接提取器:定义规则提取指定链接
运行原理:
案例:爬取读书网
一、cmd:创建CrawlSpider爬虫项目
二、item.py:确定items
1 2 3 4 5 6 7 import scrapyclass ReadbookItem (scrapy.Item): name = scrapy.Field() src = scrapy.Field()
三、dang.py:爬取信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom readbook.items import ReadbookItemclass ReadSpider (CrawlSpider ): name = 'read' allowed_domains = ['www.dushu.com' ] start_urls = ['http://www.dushu.com/book/1188_1.html' ] rules = ( Rule(LinkExtractor(allow=r'/book/1188_\d+\.html' ), callback='parse_item' , follow=True ), ) def parse_item (self, response ): img_list = response.xpath('//div[@class="bookslist"]//img' ) for img in img_list: src = img.xpath('./@data-original' ).extract_first() name = img.xpath('./@alt' ).extract_first() book = ReadbookItem(src=src, name=name) yield book
四、mysql:数据入库
1 2 3 4 5 6 7 create database spider default character set utf8 collate utf8_general_ci;use spider; create table book( id int primary key auto_increment, name varchar (128 ), src varchar (128 ) )
安装pymysql:pip install pymysql
pymysql.connect(host,port,user,password,db,charset):建立连接
conn.cursor():获取游标
.cursor.execute(sql):执行sql
五、settings.py:配置信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ROBOTSTXT_OBEY = False DB_HOST = '127.0.0.1' DB_PORT = 3306 DB_USER = 'root' DB_PASSWORD = '123456' DB_NAME = 'spider' DB_CHARSET = 'utf8' ITEM_PIPELINES = { 'readbook.pipelines.ReadbookPipeline' : 300 , 'readbook.pipelines.MysqlPipeline' : 301 } LOG_FILE = 'readbook.log'
六、pipelines.py:管道下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from itemadapter import ItemAdapterclass ReadbookPipeline : def open_spider (self, spider ): self.fp = open ('./books.json' , 'w' , encoding='utf-8' ) def process_item (self, item, spider ): self.fp.write(str (item)) return item def close_spider (self, spider ): self.fp.close() from scrapy.utils.project import get_project_settingsimport pymysqlclass MysqlPipeline : def open_spider (self, spider ): settings = get_project_settings() self.host = settings['DB_HOST' ] self.port = settings['DB_PORT' ] self.user = settings['DB_USER' ] self.password = settings['DB_PASSWORD' ] self.name = settings['DB_NAME' ] self.charset = settings['DB_CHARSET' ] self.connect() def connect (self ): self.conn = pymysql.connect( user=self.user, password=self.password, host=self.host, db=self.name, port=self.port, charset=self.charset ) self.cursor = self.conn.cursor() def process_item (self, item, spider ): sql = 'insert into book(name, src) values("{0}", "{1}")' .format (item['name' ], item['src' ]) self.cursor.execute(sql) self.conn.commit() return item def close_spider (self, spider ): self.cursor.close() self.conn.close()
日志处理 日志级别:默认的日志等级是DEBUG,只要出现了DEBUG或者DEBUG以上等级的日志那么这些日志将会打印。
CRITICAL:严重错误
ERROR: 一般错误
WARNING: 警告
INFO: 一般信息
DEBUG: 调试信息
settings.py文件设置:
LOG_FILE:将屏幕显示的信息全部记录到文件中,屏幕不再显示,注意文件后缀一定是.log。
LOG_LEVEL:设置日志显示的等级,就是显示哪些,不显示哪些。
post请求 重写start_requests方法:def start_requests(self)
start_requests的返回值:
url: 要发送的post地址
headers:可以定制头信息
callback: 回调函数
formdata: post所携带的数据,这是一个字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import scrapyclass TranslateSpider (scrapy.Spider): name = 'translate' allowed_domains = ['fanyi.baidu.com' ] def start_requests (self ): url = 'https://fanyi.baidu.com/sug' data = { 'kw' : 'translate' } yield scrapy.FormRequest(url=url, formdata=data, callback=self.translate_parse) def translate_parse (self, response ): content = response.json() print (content)
scrapy代理 settings.py中,打开一个选项:
1 2 3 DOWNLOADER_MIDDLEWARES = { 'postproject.middlewares.Proxy' : 543 , }
middlewares.py中写代码:
1 2 3 def process_request (self, request, spider ): request.meta['proxy' ] = 'https://113.68.202.10:9999' return None
scrapy中间件 selenium加持scarpy 分布式爬虫 增量式爬虫 js逆向 exe打包 python虚拟环境 通过虚拟环境工具为项目创建纯净的依赖环境,并且实现项目之间相互隔离的 Python 环境,也可以方便的切换环境中的 Python 版本。
python环境目录:
Lib:标准库和site-pakages
Script:pip安装的可执行文件
python.exe
venv 模块使用:
python -m venv myvenv:在当前目录创建虚拟环境
使用:在创建项目时引用当前创建的虚拟环境
虚拟环境激活:虚拟环境创建好后,需要激活才能在当前命令行中使用,可以理解成将当前命令行环境中 PATH 变量的值替换掉。
激活脚本路径: \Scripts\activate
退出虚拟环境:\Scripts\deactivate
为了减少环境变量的配置,虚拟环境会把python.exe和pip安装的可执行文件放在同一Scripts目录下。
pycharm创建虚拟环境:在项目创建对话框中,可以创建或者选择已经已有的解析器。
exe打包步骤 1、创建虚拟环境和开发项目
查看虚拟环境安装的第三方依赖包:pip list
导出文件形式查看虚拟环境安装的第三方依赖包:pip freeze > requirements.txt
通过 requirements.txt 导入包:pip install -r requirements.txt
安装指定库的版本:pip install selenium==3.14.0
2、进行项目开发
3、安装pyinstaller:pip install pyinstaller
4、对项目进行打包:
多文件打包:pyinstaller -D 项目名.py
单文件打包:pyinstaller -F 项目名.py
会根据打包的.py文件命名exe文件
打包并命名:pyinstaller -F 项目名.py -n 新名字
常用指令:
-F:打包一个单个文件,如果你的代码都写在一个.py文件的话,可以用这个,如果是多个.py文件建议别用
-D:打包多个文件,在dist中生成很多依赖文件,适合以框架形式编写工具代码,我个人比较推荐这样,代码易于维护
-i [icon path]:可以设置exe文件图标
-n [name]:指定可执行文件名称
–workpath:指定打包时临时文件存放目录
–distpath:指定打包好的可执行程序存放目录
–clean:在构建打包之前清理缓存并删除临时文件
打包后的文件目录:
dist:打包好的可执行程序默认存放目录
build:打包时临时文件默认存放目录
spec文件:打包配置文件
注意:打包好后拖动exe文件在cmd 里面运行,可以查看程序报错信息。
路径问题 当项目引用其他文件时,打包后运行会因为路径问题而找不到目标文件。
1、多文件打包,运行不会报错
2、单文件打包,运行会报错
因为单文件打包的exe文件运行时会解压到临时目录,代码获取的是临时目录。
解决路径问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 import osimport sysBASE_DIR1 = os.path.dirname(os.path.realpath(sys.argv[0 ])) DIR = os.path.join(BASE_DIR1, 'name.txt' ) BASE_DIR2 = os.path.dirname(os.path.abspath(__file__)) print (BASE_DIR1)print (BASE_DIR2)print (DIR)
1 2 3 4 5 6 7 import sysimport osif getattr (sys, 'frozen' , False ): BASE_DIR = os.path.dirname(sys.excutable) else : BASE_DIR = os.path.dirname(os.path.abspath(__file__))
导入模块问题 py文件打包时会默认将引入的模块一并打包,并且引入的模块中也引入了其他模块,pyinstaller也会一并打包。
如果遇到动态导入模块的代码时,打包程序不会将动态导入的模块一并打包。
需要在.spec文件的hiddenimport 中配置动态导入的模块
自定义打包:pyinstaller -F name.spec
1 2 3 4 5 import importlibkey_word = importlib.import_module('key_word' ) js = key_word.removeAttribute('aa' ) print (js)