数据解析

数据解析

  • 正则解析
  • xpath解析
  • bs4解析

正则解析

  • re.M:多行匹配
  • re.S:单行匹配(*)

爬取糗事百科中的图片数据

from urllib import request
import requests
import re

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
}

pic_url = 'https://www.qiushibaike.com/pic/page/{}/'
page_num = 5
for i in range(1, page_num + 1):
    pic_res = requests.get(pic_url.format(i), headers=headers)

    img_url_list = re.findall(r'pic\.qiushibaike\.com/system/pictures/\d+/\d+/medium/.*jpg', pic_res.text)
    for img_url in img_url_list:
        img_url = r'https://' + img_url
        img_name = img_url.rsplit('/', 1)[1]
        img_path = 'static/qiutu/' + img_name
        request.urlretrieve(img_url, img_path)

bs4解析

  • 环境的安装:
    • pip install lxml
    • pip install bs4
  • bs4解析原理:
    • 实例化一个bs对象,且将页面源码数据加载到该对象中。
    • 使用bs对象中封装好的属性或者方法实现标签定位
    • 将定位到的标签中的文本(属性)取出

常用方法 官方文档


# 搜索文档树
soup.a      # 拿到第一个X标签
soup.find()     # 可以加参数过滤,name,attrs,recursive,limit,kwargs
soup.find_all()
soup.select() # CSS选择器,官方文档: https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#id41
# 上面的操作同样适用于标签
tag.find_parent()  # 搜索当前节点的父节点
tag.parent # 获取父节点
tag.parents # 获取父节点 迭代 
tag.children    # 子孙标签
tag.descendants # 子子孙孙标签
tag.find_next_sibling()  # 搜索当前节点的后面的节点
tag.find_previous_sibling()  # 搜索当前节点的前面的节点


# 标签属性
tag.name    # 标签名
tag.get_text() # 获取标签内的值,包括子子孙孙的
tag.text  # text = property(get_text)
tag.string # 标签内的值  <h1>标签内的值</h1> 如果是 <h1>标签<h2></h2>内的值</h1> 则获取失败
.strings 和 stripped_strings
    如果tag中包含多个字符串 [2] ,可以使用 .strings 来循环获取,内部包含很多空格,使用 stripped_strings 消除

tag.has_attr('id') # 判断是否有否属性
tag['class']  #  标签属性,tag的属性操作方法与字典一样
tag.attrs['name']  # 获取值
tag.attrs['name'] = 'content'  # 设置值

爬取诗词名句网站中的三国演义小说-bs4

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup
import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
}

prefix = 'http://www.shicimingju.com'
html = requests.get('http://www.shicimingju.com/book/sanguoyanyi.html', headers=headers)

soup_sanguoyanyi = BeautifulSoup(html.text, 'lxml')
chapter_url_list = soup_sanguoyanyi.select('.book-mulu > ul > li > a')
f = open(f'static/sanguoyanyi.txt', 'w', encoding='utf8')
for i in chapter_url_list:
    chapter_url = prefix + i['href']
    html2 = requests.get(chapter_url, headers=headers)
    soup_page = BeautifulSoup(html2.text, 'lxml')
    title = soup_page.select_one('h1').text
    chapter_content = soup_page.select_one('.chapter_content').text
    f.write(title + '\n')
    f.write(chapter_content + '\n\n')
f.close()

xpath解析

  • 环境安装:
    • pip install lxml
  • 解析原理:
    • 实例化一个etree的对象,且将页面源码数据加载到该对象中
    • 调用etree对象中的xpath方法实现标签定位和数据的提取
    • 在xpath函数中必须作用xpath表达式,xpath函数返回的是一个列表

Xpath 语法 更多

/ 从根路径开始找 
// 从当前位置找
//div/a  div的直接子节点a
//div//a div的子孙节点a

属性定位:
    #找到class属性值为song的div标签
    //div[@class="song"] 
层级&索引定位:
    #找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
    //div[@class="tang"]/ul/li[2]/a
逻辑运算:
    #找到href属性值为空且class属性值为du的a标签
    //a[@href="" and @class="du"]
模糊匹配:
    //div[contains(@class, "ng")]
    //div[starts-with(@class, "ta")]
取文本:
    # /表示获取某个标签下的文本内容
    # //表示获取某个标签下的文本内容和所有子标签下的文本内容
    //div[@class="song"]/p[1]/text()
    //div[@class="tang"]//text()
取属性:
    //div[@class="tang"]//li[2]/a/@href

代码中使用xpath表达式进行数据解析:

1.下载:pip install lxml
2.导包:from lxml import etree

3.将html文档或者xml文档转换成一个etree对象,然后调用对象中的方法查找指定的节点

  2.1 本地文件:tree = etree.parse(文件名)
                tree.xpath("xpath表达式")

  2.2 网络数据:tree = etree.HTML(网页内容字符串)
                tree.xpath("xpath表达式")

爬取58二手房的房源信息

#! /usr/bin/env python
# -*- coding: utf-8 -*-
from lxml import etree
import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
}


def func(html_text, f):
    li_XP = '//ul[@class="house-list-wrap"]/li'
    title_XP = './/h2[@class="title"]/a/text()'
    sum_XP = './/p[@class="sum"]//text()'
    unit_XP = './/p[@class="unit"]//text()'
    html = etree.HTML(html_text)
    li_tag_list = html.xpath(li_XP)
    for li in li_tag_list:
        title = li.xpath(title_XP)
        sum = li.xpath(sum_XP)
        unit = li.xpath(unit_XP)
        title = ''.join(title)
        sum = ''.join(sum)
        unit = ''.join(unit)
        f.write(f'{title}\t{sum}\t{unit}\n')


url = 'https://bj.58.com/shahe/ershoufang/pn{}/'
start_pag = 1
end_pag = 5
f = open('static/58ershoufang.txt', 'a', encoding='utf8')
for i in range(start_pag, end_pag + 1):
    cur_url = url.format(i)
    html2 = requests.get(cur_url, headers=headers)
    func(html2.text, f)

f.close()

tips

  1. 安装xpath插件在浏览器中对xpath表达式进行验证:可以在插件中直接执行xpath表达式。Chrome:XPath Helper,开启和关闭的快捷键:ctrl+shift+x

  2. 返回数据乱码

    1. 修改响应对象的编码格式,response.encoding = 'utf-8'
    2. 一个通用性较强的格式,text = text.encode('iso-8859-1').decode('gbk')
  3. 爆出错误 HTTPConnectionPool(host:XX)Max retries exceeded with url:

    1. 请求频率太频繁,如何让请求结束后马上断开连接且释放池中的连接资源: 'Connection':'close'
    2. 可能ip被封,使用代理ip
  4. 安装 fiddler 出现无法监听 https 请求 , 解决方案 https://www.cnblogs.com/gigamesh/p/8124454.html,

如果还遇到无法手动添加证书 解决方案 http://blog.sina.com.cn/s/blog_492b7b280101bn6v.html

  1. 图片懒加载技术,img 标签未在可视域内是 src2 属性,加载后是 src 属性

  2. chrome debug https://developers.google.com/web/tools/chrome-devtools/javascript/?hl=zh-cn

  3. 使用xpath获取element的整个html代码, 以及 etree.toString 中文乱码问题

from lxml import etree

html = 'html源码'
tree = etree.HTML(html)
a = tree.xpath('//div[@id="testid"]')[0]
s1 = etree.tostring(a, pretty_print=True, method='html').decode('utf8')  # 将element装换为html代码,会出现中文乱码情况

s2 = etree.tostring(a, pretty_print=True, method='html', encoding='utf8').decode('utf8')  # 解决方案一

s3 = etree.tostring(a, pretty_print=True, method='html').decode('utf8')  # 解决方案二,使用html模块转码
import html
s3 = html.unescape(s3)

print(s1)
print(s2)
print(s3)

posted @ 2019-08-11 16:21  写bug的日子  阅读(93)  评论(0编辑  收藏  举报