首页 > 技术文章 > python爬虫入门 之 数据解析

lilinyuan5474 2019-09-10 15:58 原文

第四章.数据解析

  • 解析 :根据指定的规则对数据进行提取

  • 作用 :实现聚焦爬虫

  • 聚焦爬虫编码流程:

    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标签应用了伪属性

4.2.4pyquery(扩展)

推荐阅读