python爬虫中有三种解析网页的方式,正则表达式,bs4以及xpath,比较受用户喜爱的是bs4以及xpath,后期使用频率较高的是xpath,因为Scrapy框架默认使用的就是xpath解析网页数据。
数据解析方式
- 正则
- xpath
- bs4
正则 数据解析的原理:
标签的定位
提取标签中存储的文本数据或者标签属性中存储的数据
bs4解析
解析原理:
实例化一个Beautifulsoup的对象,且将页面源码数据加载到该对象中
使用该对象的相关属性和方法实现标签定位和数据提取
环境的安装:
pip install bs4 pip install lxml
实例化Beautifulsoup对象
BeautifulSoup(page_text,'lxml'):将从互联网上请求到的页面源码数据加载到该对象中
BeautifulSoup(fp,'lxml'):将本地存储的一样页面源码数据加载到该对象中
属性
soup.a.attrs 返回一字典,里面是所有属性和值
soup.a['href'] 获取href属性
文本
soup.a.string
soup.a.text
soup.a.get_text()
xpath解析:
- 解析效率比较高
- 通用性最强的
- 环境安装:pip install lxml
- 解析原理:
- 实例化一个etree对象且将即将被解析的页面源码数据加载到该对象中
- 使用etree对象中的xpath方法结合着xpath表达式进行标签定位和数据提取
- 实例化etree对象
- etree.parse('本地文件路径')
- etree.HTML(page_text)
在网页数据解析当中,re正则解析是比较费劲的,而且非常容易出错,网页数据结构一旦出错,则容易报出异常,而且想要匹配好正则,你的正则表达式需要熟练,不然你得一步步去尝试了,某些网页数据解析还真的只能用正则表达式去匹配。
如果你有接触过火车头采集器,那么对于正则,应该不会陌生!
实例的方式为大家展示Python爬虫三种解析方式
Pyhton360搜索排名查询
关于搜索排名的结果查询,前面有分享过Python百度的搜索排名查询
360搜索排名查询,大同小异,比较烦人的就是协议头的处理
#构建协议头 def ua(): ua=UserAgent() headers={ 'Cookie': 'QiHooGUID=622250714BFE7FEEBC7BE97B1768B7F1.1578470720454; _S=ilmjtet5usmi3a5005tq7of442; opqopq=d8a79fec6212514efe440041e132813c.1578470720', 'Referer': 'https://www.so.com/haosou.html', "User-Agent":ua.random, } return headers
第一个就是ua,还有就是cookies,最后一个就是Referer的添加,这个在图片的反爬中比较常见!
协议头不对的反馈,相信你也会碰到!
三种解析方式的处理,那就在细节上有小差异,同时还有部分数据的处理!
数据处理的关键点:
1.eval()函数
将str转为字典,提取排名
2.排名为空的情况
这里我用异常处理了
re正则的话写了 if else判断处理
3.xpath多个数据获取后的处理
title=''.join(li.xpath('.//a[1]//text()')[:-1]) #标题字符串处理
join函数以及切片的处理
bs4解析
#bs4获取数据 def get_bs4search(keyword,num,cxurl): jg = [] for i in range(1,int(num)+1): print(f'正在查询{i}页排名...') url="https://www.so.com/s?q=%s&pn=%d" % (keyword,i) req=get_html(url) soup=BeautifulSoup(req,"html.parser") lis=soup.find("ul",class_="result").find_all("li",class_="res-list") print(len(lis)) for li in lis: title=li.find("a").get_text() site_url=li.find("a")['href'] try: pos=eval(li.find("a")['data-res'])['pos'] #eval()函数,将str转为字典,提取排名 pm=(i-1)*10+pos except: pm="空" print(pm,title,site_url) cxjg=cxwz(keyword, i, pm, title, site_url, cxurl) if cxjg !=[]: jg.append(cxjg) time.sleep(5) print("排名查询结果:") print("-----------------------------------------") if jg == []: print("该关键词无排名!") else: print('\n'.join(jg)) print("-----------------------------------------") print("查询排名结束")
find以及find_all方法
#find方法 #find只能找到符合要求的第一个标签,他返回的是一个对象 soup.find('a') soup.find('a', class_='xxx') soup.find('a', title='xxx') soup.find('a', id='xxx') soup.find('a', id=re.compile(r'xxx')) #find_all #返回一个列表,列表里面是所有的符合要求的对象 soup.find_all('a') soup.find_all('a', class_='wang') soup.find_all('a', id=re.compile(r'xxx')) soup.find_all('a', limit=2) #提取出前两个符合要求的a
当然还有个select方法,这里我没有用,可自行采用了解!
#选择,选择器 css中
常用的选择器
标签选择器、id选择器、类选择器 层级选择器**
div h1 a 后面的是前面的子节点即可
div > h1 > a 后面的必须是前面的直接子节点
属性选择器 input[name='hehe'] select('选择器的') 返回的是一个列表,
列表里面都是对象 find find_all select不仅适用于soup对象,
还适用于其他的子对象,
如果调用子对象的select方法,
那么就是从这个子对象里面去找符合这个选择器的标签。
xpath解析
#xpath获取数据 def get_xmlsearch(keyword,num,cxurl): jg = [] for i in range(1,int(num)+1): print(f'正在查询{i}页排名...') url="https://www.so.com/s?q=%s&pn=%d" % (keyword,i) req=get_html(url) html=etree.HTML(req) lis=html.xpath('//ul[@class="result"]/li[@class="res-list"]') for li in lis: title=''.join(li.xpath('.//a[1]//text()')[:-1]) #标题字符串处理 site_url=li.xpath('.//a[1]/@href')[0] try: pos=eval(li.xpath('.//a[1]/@data-res')[0])['pos'] #eval()函数,将str转为字典,提取排名 pm=(i-1)*10+pos except: pm="空" print(pm,title,site_url) cxjg = cxwz(keyword, i, pm, title, site_url, cxurl) if cxjg != []: jg.append(cxjg) time.sleep(5) print("排名查询结果:") print("-----------------------------------------") if jg == []: print("该关键词无排名!") else: print('\n'.join(jg)) print("-----------------------------------------") print("查询排名结束")
re正则解析
#re正则获取数据 def get_research(keyword,num,cxurl): jg = [] for i in range(1,int(num)+1): print(f'正在查询{i}页排名...') url="https://www.so.com/s?q=%s&pn=%d" % (keyword,i) req=get_html(url) ul=re.findall(r'<ul class="result">(.+?)<div id="side">',req,re.S)[0] lis=re.findall(r'<li class="res-list"(.+?)</li>',ul,re.S) for li in lis: title=re.findall(r'<a href=".+?>(.+?)</a>',li,re.S)[0] title=title.replace('<em>','').replace('</em>','') #标题字符串处理 site_url=re.findall(r'<a href="(.+?)".+?>.+?</a>',li,re.S)[0] if "data-res" in li: pos=re.findall(r',"pos":(.+?),"m":',li,re.S)[0] pm = (i - 1) * 10 + int(pos) else: pm="空" print(pm,title,site_url) cxjg = cxwz(keyword, i, pm, title, site_url, cxurl) if cxjg != []: jg.append(cxjg) time.sleep(5) print("排名查询结果:") print("-----------------------------------------") if jg == []: print("该关键词无排名!") else: print('\n'.join(jg)) print("-----------------------------------------") print("查询排名结束")
运行效果:
测试就找到一个作弊站。。
附完整源码:
#360搜索排名查询 #20200108 by 微信:huguo00289 # -*- coding: utf-8 -*- import requests,re,time from fake_useragent import UserAgent from bs4 import BeautifulSoup from lxml import etree #构建协议头 def ua(): ua=UserAgent() headers={ 'Cookie': 'QiHooGUID=622250714BFE7FEEBC7BE97B1768B7F1.1578470720454; _S=ilmjtet5usmi3a5005tq7of442; opqopq=d8a79fec6212514efe440041e132813c.1578470720', 'Referer': 'https://www.so.com/haosou.html', "User-Agent":ua.random, } return headers #获取访问数据 def get_html(url): response=requests.get(url,headers=ua(),timeout=10) print(response.status_code) req=response.content.decode('utf-8') return req #获取搜索结果 #bs4获取数据 def get_bs4search(keyword,num,cxurl): jg = [] for i in range(1,int(num)+1): print(f'正在查询{i}页排名...') url="https://www.so.com/s?q=%s&pn=%d" % (keyword,i) req=get_html(url) soup=BeautifulSoup(req,"html.parser") lis=soup.find("ul",class_="result").find_all("li",class_="res-list") print(len(lis)) for li in lis: title=li.find("a").get_text() site_url=li.find("a")['href'] try: pos=eval(li.find("a")['data-res'])['pos'] #eval()函数,将str转为字典,提取排名 pm=(i-1)*10+pos except: pm="空" print(pm,title,site_url) cxjg=cxwz(keyword, i, pm, title, site_url, cxurl) if cxjg !=[]: jg.append(cxjg) time.sleep(5) print("排名查询结果:") print("-----------------------------------------") if jg == []: print("该关键词无排名!") else: print('\n'.join(jg)) print("-----------------------------------------") print("查询排名结束") #xpath获取数据 def get_xmlsearch(keyword,num,cxurl): jg = [] for i in range(1,int(num)+1): print(f'正在查询{i}页排名...') url="https://www.so.com/s?q=%s&pn=%d" % (keyword,i) req=get_html(url) html=etree.HTML(req) lis=html.xpath('//ul[@class="result"]/li[@class="res-list"]') for li in lis: title=''.join(li.xpath('.//a[1]//text()')[:-1]) #标题字符串处理 site_url=li.xpath('.//a[1]/@href')[0] try: pos=eval(li.xpath('.//a[1]/@data-res')[0])['pos'] #eval()函数,将str转为字典,提取排名 pm=(i-1)*10+pos except: pm="空" print(pm,title,site_url) cxjg = cxwz(keyword, i, pm, title, site_url, cxurl) if cxjg != []: jg.append(cxjg) time.sleep(5) print("排名查询结果:") print("-----------------------------------------") if jg == []: print("该关键词无排名!") else: print('\n'.join(jg)) print("-----------------------------------------") print("查询排名结束") #re正则获取数据 def get_research(keyword,num,cxurl): jg = [] for i in range(1,int(num)+1): print(f'正在查询{i}页排名...') url="https://www.so.com/s?q=%s&pn=%d" % (keyword,i) req=get_html(url) ul=re.findall(r'<ul class="result">(.+?)<div id="side">',req,re.S)[0] lis=re.findall(r'<li class="res-list"(.+?)</li>',ul,re.S) for li in lis: title=re.findall(r'<a href=".+?>(.+?)</a>',li,re.S)[0] title=title.replace('<em>','').replace('</em>','') #标题字符串处理 site_url=re.findall(r'<a href="(.+?)".+?>.+?</a>',li,re.S)[0] if "data-res" in li: pos=re.findall(r',"pos":(.+?),"m":',li,re.S)[0] pm = (i - 1) * 10 + int(pos) else: pm="空" print(pm,title,site_url) cxjg = cxwz(keyword, i, pm, title, site_url, cxurl) if cxjg != []: jg.append(cxjg) time.sleep(5) print("排名查询结果:") print("-----------------------------------------") if jg == []: print("该关键词无排名!") else: print('\n'.join(jg)) print("-----------------------------------------") print("查询排名结束") #查询网址是否存在排名 def cxwz(keyword,i,pm,title,site_url,cxurl): if cxurl in site_url: cxjg = f'关键词:{keyword},页码:第{i}页,排名:{pm},标题:{title},网址:{site_url}' print(f'关键词:{keyword},页码:第{i}页,排名:{pm},标题:{title},网址:{site_url}') else: cxjg=[] return cxjg if __name__=="__main__": while True: keyword = input('请输入要查询的关键词:') num = input('请输入要查询的页码数:') url = input('请输入要查询的主域名:') try: get_bs4search(keyword,num,url) except IndexError as e: print(e) print("查询排名失败!")
感兴趣的话,不妨可以尝试重写百度搜索查询或者搜狗搜索查询!
当然神马的移动搜索查询也等着试试哦!