通常来讲当咱们爬取网页的整个源代码后,是须要对网页进行解析的。css
正常的解析方法有三种html
①:正则匹配解析python
②:BeatuifulSoup解析正则表达式
③:lxml解析函数
在以前的学习中,咱们学习过爬虫的基本用法,好比/s,/d,/w,*,+,?等用法,可是在对爬取到的网页进行解析的时候,仅仅会这些基础的用法,是不够用的,所以咱们须要了解Python中正则匹配的经典函数。工具
runoob解释:re.match尝试从字符串的起始位置匹配一个模式,若是不是起始位置匹配成功的话,match就会返回none,若是匹配成功,re.match就会返回一个匹配的对象,不然也为null.查看匹配返回的对象要使用group函数的方法。post
语法:学习
re.match(pattern,string,flags=0)
解释:pattern是匹配的正则表达式,string是所要匹配的字符,而flags是标志位,用于匹配区分是否大小写,多行匹配等。具体用法以下列表格:网站
修饰符 | 描述 |
---|---|
re.I | 使得匹配对大小写不敏感 |
re.M | 多行匹配,影响^和$ |
re.S | 使得.匹配包括换行在内的全部字符(正常状况下.不匹配换行符,这个行为很危险,容易被黑客利用绕过) |
re.U | 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B |
代码案例:ui
#!/usr/bin/python #coding:utf-8 import re line="Anyone who dreams there gets" match=re.match(r'(.*) dreams (.*?).*',line,re.M|re.I) #match1=re.match(r'(.*) Dreams (.*?)',line,re.M)#大小写敏感,匹配会失败 match2=re.match(r'(.*) Dreams (.*?).*',line,re.M|re.I)#非贪婪模式 match3=re.match(r'(.*) Dreams (.*).*',line, re.M|re.I)#贪婪模式 print(match.group()) #print(match1.group()) print(match2.group()) print("match 0:",match.group(0))#Anyone who dreams there gets print("match 1:",match.group(1))#Anyone who print("match 2:",match.group(2))#' ' print("match2 0:",match2.group(0))#Anyone who dreams there gets print("match2 1:",match2.group(1))#Anyone who print("match2 2:",match2.group(2))#' ' print("match3 0:",match3.group(0))#Anyone who dreams there gets print("match3 1:",match3.group(1))#Anyone who print("match3 2:",match3.group(2))#there gets
能够看到(.*?)非贪婪模式会匹配空格,而且放入group(2),默认group()==group(0)位匹配的整句话,而groups()函数会把匹配的结果放到一个列表里。对匹配返回的结果好比match,咱们可使用start(),end(),span(),来查看开始,结束的位置。
正则匹配字符串r'(.*)'的r表明raw string,表明匹配纯粹的字符串,使用它就不会对反引号里的反斜杠进行特殊处理,好比换行符\n不会进行转义。所以咱们能够了解下正则匹配中的转义:
在不加r的前提下'\\\\'会被转义,这是由于字符串通过了两次转移,一次是字符串转义,转义成为了'\\',而后进行了正则转义。
若是加了r就会变成'\\',由于以纯粹的字符串进行匹配取消了字符串转义。这就是正则匹配中的转义流程。
re.search与re.match不一样之处在于re.search会扫描整个字符串,而且返回第一个结果,此外re.match并没有太大差异,一样可使用m.span(),m.start(),m.end()来查看起始位置等等
在以前的一篇博文:https://www.cnblogs.com/Tianwenfeigong/p/14397771.html
使用到过re.findall函数,用以对bilibili中上传Python教程的Up主进行爬取,从那篇博文中,咱们能够看出,re.findall返回的是列表,,对他的数据提取通常先用set()转换为集合的方式会更佳。
在那篇博文中,我还不懂对href进行包含但不选择,所以正则匹配的内容有限,致使正则匹配适用的环境并很少,所以从新爬取新的内容,而且引入一个新的小技巧;
目标:爬取第一个bilibili搜索栏全部的python视频的标题
代码:
#!/usr/bin/python #coding=utf-8 import requests import re link="https://search.bilibili.com/all?keyword=python&from_source=nav_search&spm_id_from=333.851.b_696e7465726e6174696f6e616c486561646572.9" headers={"User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0'} r=requests.get(link,headers=headers) text=r.text title_list=re.findall('<a title=(.*?) href=.*?>',text) for i in title_list: print(i)
重点内容:title_list=re.findall('<a title=(.*?) href=.*?>',text)
第二个.?表示匹配知足条件的结果,第一个(.?)表示对知足条件的结果进行提取
经过这个方式咱们就能够提取标题了
BeautifulSoup能够从HTML或者XML中提取数据,相似一个工具箱,使用BeautifulSoup时,可使用一些解析器使得速度快一点呢,目前比较好的的Lxml HTML解析器,使用方法以下:
soup=BeautifulSoup(r.text,"lxml")
BeautifulSoup的本质是复杂的树形结构,对他的对象提取通常有三种方法:
①:遍历文档树,遍历文档树相似于Linux的文件目录,既然是树形结构必定是从上到下的因此第一个节点必定是header,具体方法以下:
#如要得到以<h1>开头的标签,则可使用 soup.header.h1 #会返回相似于<h1 id="name">CuLin</h1>的内容 #若是要得到div的全部子节点,则可使用 soup.header.div.contents #若是要得到div的全部孩子节点,则可使用 soup.header.div.children #若是要得到div的子子孙孙节点,则可使用 soup.header.div.descendants #若是要得到节点a的父节点,则可使用 soup.header.div.a.parents
②:搜索文档树:
遍历文档树的局限性很大,通常不是很推荐使用,更多的仍是搜索文档树。使用方法很便捷,精髓在find和find_all函数,好比咱们要爬取博客文章的标题:
#!/usr/bin/python #coding:utf-8 import requestss from bs4 import BeautifulSoup link="http://www.santostang.com/" r=requests.get(link) soup=BeautifulSoup(r.text,"lxml") #打印第一篇标题 first_title=soup.find("h1",class_="post-title").a.text.strip() #打印全部文章的 all_title=soup.find_all("h1",class_="post-title") for i in range(len(all_titile)): print("No ",i," is ",all_title[i].a.text.strip())
③: CSS选择器:
Css能够做为遍历文档树鼩提取数据,也能够按照搜索文档树的方法去搜索,可是我我的以为css选择器不如selenium好用,而且使用上很不方便,因此再也不赘述
此外lxml解析器重点是xpath,这在selenium也有很好的体现,而xpath的选择咱们能够在网页检查中直接检查xpath更为方便。
爬取贝壳网站南京房源信息,总房价,平均房价,地点
#coding:utf-8 import requests import re from bs4 import BeautifulSoup link="https://nj.ke.com/ershoufang/pg" headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0","Host": "nj.ke.com"} for i in range(1,20): link_=link+str(i)+'/' r=requests.get(link_,headers=headers) soup=BeautifulSoup(r.text,"html.parser") houst_list=soup.find_all('li',class_='clear') for house in houst_list: name=house.find('div',class_='title').a.text.strip()+'\n' houst_info=house.find('div',class_='houseInfo').text.strip() houst_info=re.sub('\s+','',houst_info)+'\n' total_money=str(house.find('div',class_='totalPrice').span.text.strip())+'万' aver_money=str(house.find('div',class_='unitPrice').span.text.strip()) adderess=house.find('div',class_='positionInfo').a.text.strip() text_="标题:"+name+"房屋信息:"+houst_info+"总价:"+total_money+' '+aver_money+adderess with open ('haha.txt',"a+") as f: f.write(text_)
结果返回一个haha.txt的文本,内容以下: