- 正则(几乎不用)
- bs4
- xpath(***)
- pyquery(自学)
在爬虫中为什么需要使用数据解析?
- 数据解析作用:帮助我们可以实现聚焦爬虫(爬取局部数据)
-
聚焦爬虫实现流程:
- 1.指定url
- 2.发起请求
- 3.获取响应数据
- 4.数据解析
- 5.持久化存储
-
数据解析通用原理
-
1.html作用?
- html主要的作用就是用来展示数据
-
2.我们要解析的数据其实都是存储在
- html的标签之中
- 标签的属性
-
1.html作用?
-
通用原理:
- 定位标签
- 获取标签中的数据或者标签的属性值
- 环境安装:pip install bs4,pip install lxml
-
bs4解析原理
- 实例化一个BeautifulSoup的对象,且将被解析的页面数据加载到该对象中
- 调用BeautifulSoup对象中相关的方法和属性进行标签定位和相关数据的提取
-
BeautifulSoup的对象实例化方式:
-
方式1:BeautifulSoup(fp,\'lxml\')
- 可以将本地存储的html文件中的数据进行数据解析
- 方式2:BeautifulSoup(page_text,\'lxml\')
- 可以将互联网上请求到的页面数据进行数据解析
-
方式1:BeautifulSoup(fp,\'lxml\')
-
标签定位:
- soup.tagName:可以定位到第一次出现的该标签
-
属性定位:
-
soup.find(\'tagName\',attrName=\'attrValue\')
- 注意:find只可以定位到第一次出现的该标签
-
soup.find_all(\'tagName\',attrName=\'attrValue\')
- 注意:find_all可以定位到符合条件所有的标签,返回值一定是列表
-
soup.find(\'tagName\',attrName=\'attrValue\')
-
选择器定位:select(\'选择器\')
- id,类,层级选择器
-
层级选择器:
- 大于号:表示一个层级
- 空格:表示多个层级
-
取文本
- tag.string:获取标签直系的文本内容
- tag.text:获取标签下所有的文本内容
-
取属性
- tag[\'attrName\']
from bs4 import BeautifulSoup fp = open(\'./test.html\',\'r\',encoding=\'utf-8\') soup = BeautifulSoup(fp,\'lxml\') soup.div soup.find(\'div\',class_="song") soup.find_all(\'div\',class_=\'song\') soup.find_all(\'a\',id=\'feng\') soup.select(\'#feng\') soup.select(\'.song\') soup.select(\'.tang > ul > li > a\') soup.select(\'.tang a\') [<a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a>, <a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a>, <a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a>, <a class="du" href="http://www.sina.com">杜甫</a>, <a class="du" href="http://www.dudu.com">杜牧</a>, <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>] soup.find(\'a\',id="feng").text \'凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘\' soup.find(\'div\',class_=\'song\').text \'\n李清照\n王安石\n苏轼\n柳宗元\n\nthis is span\n\t\t宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱\n总为浮云能蔽日,长安不见使人愁\n\n\' soup.find(\'a\',id="feng")[\'href\'] \'http://www.haha.com\'
-
需求:将三国演义整片内容爬取和保存
import requests from bs4 import BeautifulSoup headers = { \'User-Agent\':\'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36\' } fp = open(\'sanguo.txt\',\'w\',encoding=\'utf-8\') url = \'http://www.shicimingju.com/book/sanguoyanyi.html\' page_text = requests.get(url=url,headers=headers).text #数据解析:章节标题+详情页的url soup = BeautifulSoup(page_text,\'lxml\') a_list = soup.select(\'.book-mulu > ul > li > a\') for a in a_list: title = a.string detail_url = \'http://www.shicimingju.com\'+a[\'href\'] #对详情页的url发起请求获取详情页的页面源码数据 page_text_detail = requests.get(url=detail_url,headers=headers).text #数据解析:章节内容 detail_soup = BeautifulSoup(page_text_detail,\'lxml\') content = detail_soup.find(\'div\',class_=\'chapter_content\').text fp.write(title+\':\'+content) print(title,\'下载保存成功!!!\') fp.close()
xpath解析
-
-
环境安装:pip install lxml
-
xpath解析的原理:
-
html标签是基于树状的结构存在的
-
1.实例化一个etree的对象,且将被解析的数据加载到该对象
-
2.调用etree对象中的xpath方法结合着不同形式的xpath表达式进行标签定位和数据提取
-
-
etree对象的实例化两种方式:
-
方式1:etree.parse(\'filename\')
-
方式2:etree.HTML(page_text)
-
-
-
标签定位
-
最左侧的/:一定要从根标签开始进行指定标签的定位
-
非最左侧的/:表示一个层级
-
非最左侧的//:表示多个层级
-
最左侧的//:可以从任意位置定位指定标签(常用)
-
属性定位://tagName[@attrName="attrValue"]
-
索引定位://tagName[index],注意索引是从1开始
-
模糊匹配:
-
//div[contains(@class, "ng")]:定位到class属性值中包含ng的div标签
-
//div[starts-with(@class, "ta")]:定位到class属性值是以ta开头的标签
-
-
-
取文本
-
/text():可以将标签中直系的文本内容取出,且返回的列表元素只有一个
-
//text():可以将标签中所有的文本内容取出,且返回的列表元素为多个
-
-
取属性
-
/@attrName
-
from lxml import etree tree = etree.parse(\'test.html\') tree <lxml.etree._ElementTree at 0x11387ad48> #定位title标签 tree.xpath(\'/html/head/title\') tree.xpath(\'/html//title\') tree.xpath(\'//title\') [<Element html at 0x113db1ec8>] #定位class属性值为cong的div标签 tree.xpath(\'//div[@class="song"]\') [<Element div at 0x113fa00c8>] tree.xpath(\'//a[@id="feng"]/text()\')[0] \'凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘\' \'\'.join(tree.xpath(\'//div[2]//text()\')) \'\n\t\t李清照\n\t\t王安石\n\t\t苏轼\n\t\t柳宗元\n\t\t\n\t\t\tthis is span\n\t\t宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱\n\t\t总为浮云能蔽日,长安不见使人愁\n\t\t\n\t\' tree.xpath(\'//a[@id="feng"]/@href\')[0] \'http://www.haha.com\'
-
爬取糗事百科的段子内容
url_model = \'https://www.qiushibaike.com/text/page/%d/\' for page in range(1,14): url = format(url_model%page) page_text = requests.get(url=url,headers=headers).text #数据解析:段子作者+内容 tree = etree.HTML(page_text) #定位标签 div_list = tree.xpath(\'//div[@class="col1 old-style-col1"]/div\') for div in div_list: #局部的数据解析 #每次循环使用的div表示就是页面中定位到的指定div标签(包含了要解析的内容) #注意:./就是xpath方法的调用者 author = div.xpath(\'./div[1]/a[2]/h2/text()\')[0] #想要从div表示的指定标签数据中进行指定数据的解析 content = div.xpath(\'./a[1]/div/span//text()\') content = \'\'.join(content) print(author,content)
-
拓展:
-
可以将文字数据进行语音合成
-
可以使用百度AI进行语音合成
-
-
-
爬取图片数据
import os dirName = \'GirlsLib\' if not os.path.exists(dirName): os.mkdir(dirName) url_model = \'http://pic.netbian.com/4kmeinv/index_%d.html\' for page in range(1,6): if page == 1: url = \'http://pic.netbian.com/4kmeinv/\' else: url = format(url_model%page) response = requests.get(url=url,headers=headers) response.encoding = \'gbk\' page_text = response.text #数据解析:图片的名称+图片的地址 tree = etree.HTML(page_text) li_list = tree.xpath(\'//*[@id="main"]/div[3]/ul/li\') for li in li_list: title = li.xpath(\'./a/img/@alt\')[0]+\'.jpg\' img_src = \'http://pic.netbian.com\'+li.xpath(\'./a/img/@src\')[0] #对图片地址发起请求 img_data = requests.get(url=img_src,headers=headers).content img_path = dirName+\'/\'+title with open(img_path,\'wb\') as fp: fp.write(img_data) print(title,\'写入成功!\')
-
想要解析出携带标签的文本内容,如何实现?
-
-
爬取雪球网中的咨询数据
import requests headers = { \'User-Agent\':\'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36\', } url = \'https://xueqiu.com/statuses/hot/listV2.json?since_id=-1&max_id=72813&size=15\' requests.get(url,headers=headers).json() #问题:没有获取数据,原因一定是没有严格模拟浏览器上网的流程
-
在爬虫中处理cookie的方式:
-
手动处理
-
从抓包工具中将cookie封装到headers字典中
-
局限性:如果cookie过了有效时长,则该种方式会失效
-
-
-
自动处理
-
使用session机制。
-
1.可以获取一个session对象
-
2.可以基于该对象进行请求的发送
-
在请求发送的过程中,如果产生了cookie,则cookie会被存储到该对象中。
-
如果cookie被存储到了session对象中,则再次使用session对象发请求,则该次请求就是携带者cookie进行的请求发送
-
-
注意:Session对象在使用的时候,至少需要被调用两次。
-
-
sess = requests.Session() #创建了一个sessio对象 first_url = \'https://xueqiu.com/\' #该次请求会产生cookie,且cookie存储到了session对象中 sess.get(url=first_url,headers=headers) url = \'https://xueqiu.com/statuses/hot/listV2.json?since_id=-1&max_id=72813&size=15\' #该次请求就是携带cookie进行的请求发送 sess.get(url,headers=headers).json()
代理反爬
-
代理反爬
-
什么是代理
-
就是代理服务器
-
-
代理服务器的作用:
-
可以进行请求和响应的转发/拦截
-
-
在爬虫中为何需要使用代理?
-
如果我们使用爬虫对一个网站在段时间内发起一个高频的请求,该网站会检测出这个异常的现象,且会将异常的请求ip获取,将ip加入到黑名单,然后该ip在近期就无法再次对该网站进行网络访问。
-
如果本机ip被对方服务器加入到了黑名单,则我们就可以使用代理服务器进行请求转发,最终对方服务器获取的请求ip就是代理服务器的不在我们自己本机的。
-
-
代理的匿名度:
-
透明:对方服务器知道你使用了代理,也知道你的真实ip
-
匿名:对方服务器知道你使用了代理,但是不知道你的真是ip
-
高匿:不知道使用了代理,也不知道你的真实ip
-
-
代理的类型
-
http:只能转发http协议的请求
-
https:只能转发https协议的请求
-
-
from lxml import etree #没有使用代理的情况 url = \'https://www.sogou.com/web?query=ip\' page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) tree.xpath(\'//*[@id="ipsearchresult"]//text()\')
#使用代理的情况 url = \'https://www.sogou.com/web?query=ip\' page_text = requests.get(url=url,headers=headers,proxies={\'https\':\'114.239.119.157:42559\'}).text tree = etree.HTML(page_text) tree.xpath(\'//*[@id="ipsearchresult"]//text()\')
-
需求:
-
对一个网站发起高频请求,然后让其将本机ip加入黑名单,构建代理池处理
-
import random #提取代理精灵代理服务器ip+port的方法 all_proxy = [] #代理池 url = \'http://ip.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=51&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2\' page_text = requests.get(url,headers=headers).text tree = etree.HTML(page_text) data_list = tree.xpath(\'//body//text()\') for data in data_list: dic = {} dic[\'https\'] = data all_proxy.append(dic) url_model = \'http://www.521609.com/daxuemeinv/list8%d.html\' all_data = [] for page in range(1,23): url = format(url_model%page) page_text = requests.get(url=url,headers=headers,proxies=random.choice(all_proxy)).text tree = etree.HTML(page_text) li_list = tree.xpath(\'//*[@id="content"]/div[2]/div[2]/ul/li\') for li in li_list: detail_url = \'http://www.521609.com\'+li.xpath(\'./a[1]/@href\')[0] page_text_detail = requests.get(detail_url,headers=headers).text all_data.append(page_text_detail) print(len(all_data))
作业
-
爬取站长素材中的简历模板