python爬虫入门 之 数据解析
第四章.数据解析
- 
解析 :根据指定的规则对数据进行提取 
- 
作用 :实现聚焦爬虫 
- 
聚焦爬虫编码流程: 1.指定url
 2.发起请求
 3.获取响应数据
 4.数据解析
 5.持久化存储
4.1数据解析通用原理
- 
数据解析作用地点 - 
页面源码(一组html标签组成的) 
 
- 
- 
html标签核心作用 - 
用于展示数据 
 
- 
- 
html是如何展示数据的 - 
html所要展示的数据一定是被放置在html标签中,或者是在属性中 
 
- 
- 
通用原理 : 1.标签定位. 2.取文本或取属性 
4.2四种数据解析的方式
4.2.1 正则
- 
需求 : 爬取xx百科中糗图数据 链接地址 : https://www.qiushibaike.com 两种爬取方式 #方式一:
 import requests
 #即将发起请求对应的头信息
 headers = {
 "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
 }
 url = "https://pic.qiushibaike.com/system/pictures/12217/122176374/medium/XBOP3Y2YQM1SEEXA.jpg"
 img_data = requests.get(url=url,headers=headers).content #content返回的是 byte 类型的数据
 with open("./123.jpg",'wb') as f:
 f.write(img_data)#方式二:
 from urllib import request
 url = "https://pic.qiushibaike.com/system/pictures/12217/122176374/medium/XBOP3Y2YQM1SEEXA.jpg"
 request.urlretrieve(url,"./456.jpg")- 
方式一 和 方式二 对于图片操作最大的不同之处是什么? - 
方式二不可以使用UA伪装的机制 
 
- 
 单页数据的爬取 #糗事百科
 import re
 import os
 from urllib import request
 
 dir_name = "./qiutu"
 if not os.path.exists(dir_name):
 os.mkdir(dir_name)
 
 url = "https://www.qiushibaike.com/pic/"
 page_text = requests.get(url=url,headers=headers).text
 #数据解析,图片地址
 #正则表达式
 ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
 img_src_list = re.findall(ex,page_text,re.S) # re.S单行匹配
 for src in img_src_list:
 src = "https:" + src
 img_name = src.split("/")[-1]
 #图片存储地址
 img_path = dir_name + "/" + img_name
 #对图片地址单独发起请求获取文件数据
 request.urlretrieve(src,img_path)
 print(img_name,"下载成功")爬取分页 分析每个页码对应的url是有共性的:https://www.qiushibaike.com/pic/page/3/?s=5222981 #糗事百科
 import re
 import os
 from urllib import request
 
 dir_name = "./qiutumany"
 if not os.path.exists(dir_name):
 os.mkdir(dir_name)
 
 #指定一个通用模板(不可变)
 url = "https://www.qiushibaike.com/pic/page/%d/"
 
 for page in range(1,5):
 print("正在打印第{}页的数据".format(page))
 #形成某页码完整url
 new_url = format(url%page)
 page_text = requests.get(url=new_url,headers=headers).text
 #数据解析,图片地址
 #正则表达式
 ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
 img_src_list = re.findall(ex,page_text,re.S) # re.S单行匹配
 for src in img_src_list:
 src = "https:" + src
 img_name = src.split("/")[-1]
 #图片存储地址
 img_path = dir_name + "/" + img_name
 #对图片地址单独发起请求获取文件数据
 request.urlretrieve(src,img_path)
- 
模块 :urllib
- 
urllib就是一个比较老的网络请求模块,在requests没出现之前,请求发送的都是urllib 
4.2.2 bs4
#环境的安装
pip install bs4
pip install lxml
#解析原理
    1.实例化一个BeautifulSoup对象,并且将即将被解析的页面源码数据加载到该对象中
    2.调用BeautifulSoup对象的相关属性和方法来进行标签定位和数据提取
#如何实例化BeautifulSoup对象
    BeautifulSoup(fp,"lxml")    #专门用于解析本地存储的html文档中的数据
    BeautifulSoup(page_text,"lxml")  #专门用于将互联网请求到的页面源码数据进行解析
标签定位
soup.tagName   : 定位到第一个tagName标签,返回的是单数
#属性定位:
    soup.find(tagName,attrName="value")  返回的是单数
    soup.find_all(tagName,attrName="value")  返回的是列表
#选择器定位:
    select("选择器")  返回的是列表
     标签,类,i, 层级   > :一个层级  空格:多个层级  
from bs4 import BeautifulSoup
fp = open("./test.html",'r',encoding='utf-8')
soup = BeautifulSoup(fp,"lxml")    #将即将被解析的页面源码加载到对象中
soup.p    #<p>百里守约</p>
#find用于属性定位
soup.find("div")       
soup.find("div",class_="song")
soup.find_all("div",class_="song")
soup.select(".tang")
soup.select("#feng")
soup.select(".tang > ul > li ")
li_6 = soup.select(".tang > ul > li")[6]    #<li><i>度蜜月</i></li>
li_6.i          #<i>度蜜月</i>
li_6.i.text     #'度蜜月'
li_6.i.string   #'度蜜月'
soup.find("a",id="feng")
soup.find("a",id="feng")["href"]
- 
soup.find("div") 
- 
soup.find("div",class_="song")_ 
- 
soup.find_all("div",class_="song") 
- 
类选择器 :soup.select(".tang") 
- 
id选择器 :soup.select("#feng") 
- 
层级选择器 : soup.select(".tang > ul > li ") 
- 
soup.find("a",id="feng") 
- 
soup.find("a",id="feng")["href"] 
取数据
#取文本
    tag.string   标签中直系的文本内容
     tag.text     标签中所有文本内容  
#取属性
- 
需求:爬取三国演义整篇小说内容 - 
章节名称 
- 
章节内容 
 import requests
 #在首页中解析章节名称和每个章节详情页的url
 url = "http://www.shicimingju.com/book/sanguoyanyi.html"
 headers = {
 "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
 }
 page_text = requests.get(url=url,headers=headers).text
 page_text
 soup = BeautifulSoup(page_text,"lxml")
 a_list = soup.select(".book-mulu > ul > li > a")
 fp = open("sanguo.txt",'w',encoding="utf-8")
 for a in a_list:
 detail_url = "http://www.shicimingju.com"+ a["href"]
 title = a.text
 #对章节详情页的url发起请求,解析详情页的章节内容
 detail_page_text = requests.get(url=detail_url,headers=headers).text
 soup = BeautifulSoup(detail_page_text,"lxml")
 chap_content = soup.find("div",class_="chapter_content").text
 fp.write(title + ":"+chap_content)
 print(title,"爬取成功")
 fp.close()
- 
4.2.3 xpath
#环境的安装: pip install lxml
#xpath解析原理:
    1.实例化一个etree类型的对象,且将页面源码加载到该对象中
    2.需要调用该对象的xpath方法结合不同形式的xpath表达式进行标签定位和数据提取 
#etree对象的实例化
    etree.parse(fileName)           #用于解析本地存储的html文档中的数据
    etree.HTML(page_text)           #用于将互联网请求到的页面源码数据进行解析
#xpath返回的永远是一个列表
标签定位
1.xpath表达式中"最"最左边的"/"表示的含义是,当前定位的标签必须从根节点开始进行定位
2.xpath表达式中"最"最左边的"//"表示可以从任意位置进行标签定位
3.xpath表达式中"非"最左边的"/"表示的是一个层级
4.xpath表达式中"非"最左边的"//"表示的是多个层级
#属性定位:
    //tageName[@class='value']
#索引定位:
提取数据
#取文本
    /text()   取直系的文本
    //text()   取所有文本内容
#取属性
    tag/@attrName
from lxml import etree
tree = etree.parse("./test.html")
tree.xpath("/html/head/meta")   #[<Element meta at 0x84fbb08>] -->绝对路径
tree.xpath("//meta")[0]         #[<Element meta at 0x8523748>] -->相对路径,将页面中所有的meta进行定位
tree.xpath("/html//meta")[0]  
#属性定位
tree.xpath('//div[@class="song"]') 
#索引定位
tree.xpath('//div[@class="tang"]/ul/li[3]')  #该索引从 1 开始
#取文本
tree.xpath('//p[1]/text()')   #['百里守约', '李清照']
tree.xpath('//div[@class="song"]//text()') 
#取属性  
tree.xpath('//a[@id="feng"]/@href')  #['http://www.haha.com']
- 
需求 :爬取xx直聘招聘信息 - 
岗位名称 
- 
公司名称 
- 
薪资 
- 
岗位描述 
 import requests
 from lxml import etree
 headers = {
 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
 'cookie':'lastCity=101010100; __c=1566877560; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1566877561; _uab_collina=156687756118178796315757; __l=l=%2Fwww.zhipin.com%2F&r=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DidbSvNzz2fLSl1WXiEmtINauVHUZYSNqejHny725pc5RTwaHqh5uDx1LewpyGmaT%26wd%3D%26eqid%3Dbadf667700040677000000025d64a772&friend_source=0&friend_source=0; __zp_stoken__=91d9QItKEtUk5dMMnDG7lwzq8mBW1g%2FkEsFOHXIi%2FwMd%2FPRRXc%2FPMKjsDYwsfC4b7vAT3FVnTmYBjGp8gW1OeZ5TdA%3D%3D; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1566879753; __a=69160831.1566877560..1566877560.16.1.16.16'
 }
 url = 'https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&city=101010100&industry=&position='
 page_text = requests.get(url,headers=headers).text
 #数据解析
 tree = etree.HTML(page_text)
 li_list = tree.xpath('//div[@class="job-list"]/ul/li')
 for li in li_list:
 # 需要将li表示的局部页面源码数据中的相关数据进行提取
 # 如果xpath表达式被作用在了循环中,表达式要以./或者.//开头
 detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/h3/a/@href')[0]
 job_title = li.xpath('.//div[@class="info-primary"]/h3/a/div/text()')[0]
 salary = li.xpath('.//div[@class="info-primary"]/h3/a/span/text()')[0]
 company = li.xpath('.//div[@class="info-company"]/div/h3/a/text()')[0]
 #对详情页的url发请求解析出岗位职责
 detail_page_text = requests.get(detail_url,headers=headers).text
 tree = etree.HTML(detail_page_text)
 job_desc = tree.xpath('//div[@class="text"]//text()')
 job_desc = ''.join(job_desc)
 
 print(job_title,salary,company,job_desc)
- 
- 
需求 :爬取xx百科的段子内容和作者名称 链接地址 :https://www.qiushibaike.com/text/page/3/ import requests
 from lxml import etree
 
 headers = {
 "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
 }
 url = "https://www.qiushibaike.com/text/page/4/"
 page_text = requests.get(url=url,headers=headers).text
 tree = etree.HTML(page_text)
 div_list = tree.xpath("//div[@id='content-left']/div")
 for div in div_list:
 author = div.xpath("./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()")[0]
 content = div.xpath("./a[1]/div[@class='content']/span//text()")
 content = "".join(content)
 print(author,content)#总结:
 另一种较为通用的xpath表达式的使用形式: #管道符"|"在xpath表达式中的应用
 #用处 : 用于解析不规则布局的页面数据
中文乱码处理问题
import requests
from lxml import etree
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
}
#指定一个通用的url模板
url = 'http://pic.netbian.com/4kmeishi/index_%d.html'
for page in range(1,3):
    if page == 1:
        new_url = 'http://pic.netbian.com/4kmeishi/'
    else:
        new_url = format(url%page)
    response =  requests.get(new_url,headers=headers)
    #response.encoding = 'utf-8'
    page_text = response.text
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li')
    for li in li_list:
        img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
        img_name = li.xpath('./a/b/text()')[0]
        img_name = img_name.encode('iso-8859-1').decode('gbk')
ConnectionPool错误
#原因一.在短时间内向网站发起了一个高频的请求
    解决方式 :使用代理
#原因二.连接池(http)中的资源被耗尽
    立即将请求断开 Connection:close
xpath和bs4最明显的区别
#解决除携带标签的局部内容
    bs4相关标签定位的方法或属性 返回值就是携带标签的内容
反爬机制之三 :图片懒加载
在img标签应用了伪属性
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号