python爬虫学习(数据解析)

引入

我们首先回顾一下之前学习的requsets模块实现数据爬取的流程:

  • 指定url
  • 发起请求
  • 获取响应数据
  • 持久化存储

其实,在上述的流程中还需要较为重要的一步,就是在持久化存储之前需要进行指定数据解析。因此大多数情况下的需求,我们都会指定去使用聚焦爬虫,也就是爬取页面中指定部分的数据值,而不是整个页面的数据。因此,本次可能将详细介绍三种聚焦爬虫中的数据解析方式。至此我们的数据爬取的流程可以修改为:

  • 指定url
  • 发起请求
  • 获取响应数据
  • 数据解析
  • 持久化存储

聚焦爬虫

我们之前介绍的是通用爬虫,它用于爬取整张页面信息
聚焦爬虫:聚焦爬虫是建立在通用爬虫的基础之上的,再将我们所需要的数据进行提取(数据解析),从而实现爬取页面中指定的页面内容;

数据解析的分类

  • 基于正则实现的数据解析
  • 通过bs4实现的数据解析
  • 使用xpath实现数据解析(最通用的)

数据解析原理概述

  • 解析的局部的文本内容都会在标签之间或者对应的属性中进行储存
  • 1 .进行指定标签的定位
  • 2.标签或者对应的属性中储存的数据值进行提取(解析)

使用正则表达式进行数据解析

在实现这个我们需求时,先解决一个问题,就是怎样爬取一种图片:

import requests
if __name__ == "__main__":
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'}

    url = 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3187129139,3162464776&fm=26&gp=0.jpg'
    # 使用content返回的是二进制形式的图片数据
    img_data = requests.get(url=url).content

    with open('./qiutu.jpg', 'wb') as fp:
        fp.write(img_data)
  • 需求:爬取糗事百科中糗图板块下所有的糗图图片
import requests
import re
import os
if __name__ == '__main__':
	# 创建一个文件夹用于保存所有的图片
	if not os.path.exists('./qiutulibs'):
		os.mkdir('./qiutulibs')
	url = 'http://www.qiushibaike.com/pic/'
	headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'}
	# 使用通用爬虫对url对应的一整张页面进行爬取
	page_text = requests.get(url=url, headers=headers).text
	# 使用聚焦爬虫将页面中所有的糗图进行解析/提取
	# 根据网页文本的格式编写正则表达式,提取出图片的url地址
	ex = '
.*?
'
# 获得所有图片url的列表 img_src_list = re.findall(ex, page_text, re.S) for src in img_src_list: src = 'https' + src # 拼接一个完整的图片地址 img_data = requests.get(url=src, headers=headers).content # 生成图片名称 img_name = src.split('/')[-1] imgPath = 'qiutulips/' + img_name with open(imgPath, 'wb') as fp: fp.write(img_data)

使用bs4进行数据解析

实现原理

  • 实例化一个BeautifuiSoup对象,并且将页面源码数据加载到该对象中;
  • 通过调用BeautifuiSoup对象中相关的属性或者方法进行标签定位和数据提取;

环境安装

  • pip install bs4
  • pip install lxml

如何实例化BeautifuiSoup对象

  • 将本地的html文档中的数据加载到该对象中;
from bs4 import BeautifulSoup

if __name__ == '__main__':
    # 将本地的html文档中的数据加载到该对象中
    fp = open('./text.html', encoding='utf-8')
    soup = BeautifulSoup(fp, 'lxml')
    print(soup)
  • 将互联网上获取的页面源码加载到该对象中;
from bs4 import BeautifulSoup

if __name__ == '__main__':
    # 将本地的html文档中的数据加载到该对象中
    page_text = response.text
    soup = BeautifulSoup(page_text, 'lxml')
    print(soup)

提供的相关的属性和方法

from bs4 import BeautifulSoup

if __name__ == '__main__':
    # 将本地的html文档中的数据加载到该对象中
    fp = open('./text.html', encoding='utf-8')
    soup = BeautifulSoup(fp, 'lxml')
    soup.tagName  									# 返回的是文档中第一次出现的tagName标签
    soup.find(tagName, class_=classname)			# 第一种用法:返回的是文档中第一次出现的tagName标签
    												# 第二种用法:属性定位
    soup.find_all(tagName, class_=classname)		# 第一种用法:返回的是文档中出现的所有tagName标签(列表)
    												# 第二种用法:属性定位
	soup.select('某种选择器(id,class,标签...选择器)')	# 返回一个列表
	soup.select('.tang > ul > li > a')    			# 层级选择器,>表示的是一个层级
	soup.select('.tang > ul > li a')    			# 层级选择器,空格表示的是多个层级
	
	# 获取标签之间的文本内容
	soup.select('.tang > ul > li a').text			# 获取标签中所有的文本内容
	soup.select('.tang > ul > li a').string			# 只获取该标签下面直系的文本内容
	soup.select('.tang > ul > li a').get_text()
	# 获取标签中属性值
	soup.a['herf']

bs4解析案例实战

  • 需求:爬取三国演义小说所有的章节和所有内容(http://www.shicimingju.com/book/sanguoyanyi.html)
import requests
from bs4 import BeautifulSoup
if __name__ == '__main__':

    # 获取页面数据
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'}
    url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
    page_text = requests.get(url=url, headers=headers).text

    # 在首页中解析出章节标题和详情页的url
    # 实例话BeautifulSoup对象,需要将页面源码数据加载到该对象中
    soup = BeautifulSoup(page_text, 'lxml')
    # 解析章节标题和详情页的url
    li_list = soup.select('.book-mulu > ul > li')
    fp = open('./sanguo.txt', 'w', encoding='utf-8')
    for li in li_list:
        title = li.a.string
        detail_url = 'http://www.shicimingju.com' + li.a['href']
        # 对详情页发起请求,解析出章节内容
        detail_page_text = requests.get(url=detail_url, headers=headers).text
        # 解析出详情也中相关页的内容
        detail_soup = BeautifulSoup(detail_page_text, 'lxml')
        div_tag = detail_soup.find('div', class_='chapter_content')
        # 解析到了章节的内容
        content = div_tag.text
        fp.write(title + ':\n' + content + '\n')
        print(title, '爬取成功!!!')

使用xpath解析(最常用且最高效的一种解析方式)

xpath解析原理

  • 实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象
  • 调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的获取。

环境的安装

pip install lxml

如何实例化一个etree对象(from lxml import etree)

  • 将本地的html文档中的源码数据加载到etree对象中:
# 实例化一个etree对象:
from lxml import etree

etree.parse(filePath)
  • 可以将从互联网上获取的源码数据加载到该对象中:
# 实例化一个etree对象:
from lxml import etree

etree.HTML('page_text')
  • xpath(‘xpath表达式’)
    我们一般只会用到etree对象中的xpath一种方法,从文本中获取内容是通过变化xpath表达式来实现的

xpath表达式

from lxml import etree
if __name__ == '__main__':
	# 实例化了一个etree对象,并将被解析的源码加载到了该对象中
	tree = etree.parse('filePath')
	# 定位标签相关方法
	r = tree.xpath('/html/head/title')			# 表示根据层级获取文本内容,返回的是一个对象列表
												# 第一个/表示是从根节点开始,往后的每一/表示一个层级
	r = tree.xpath('/html//div')				# //表示多个层级
	r = tree.xpath('//div')						# //表示定位所有的div标签内容
	r = tree.xpath('//div[@attrName="attrValue"]')		# 定位到属性名为song下的所有div标签文本内容(属性定位)
	r = tree.xpath('//div[@attrName="attrValue"]/p'[3])	# 索引定位
	
	# 提取标签中的文本
	r = tree.xpath('//div[@attrName="attrValue"]//li[5]/a/text()')	# /text()方法可以获取标签中的文本(直系的)
	r = tree.xpath('//div[@attrName="attrValue"]//li[5]/a//text()')# //text()方法可以获取标签中的文本(所有的)
	
	# 提取属性
	r = tree.xpath('//div[@attrName="attrValue"]//img/@attrName')

xpath案例

  • 需求:爬取58二手房中的房源信息
import requests
from lxml import etree

if __name__ == '__main__':
    # 爬取到页面源码数据
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'}
    url = 'https://gz.58.com/ershoufang/'
    page_text = requests.get(url=url, headers=headers).text

    # 数据解析
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
    # print(li_list)
    fp = open('58.txt', 'w', encoding='utf-8')
    for li in li_list:
        # 页面数据当中局部解析
        title = li.xpath('./div[2]/h2/a/text()')[0]     # ./表示的就是xpath左侧的li
        print(title)
        fp.write(title+'\n')
  • 需求:从网站上爬取图片(http://pic.netbian.com/4kyingshi/)
import requests
from lxml import etree
import os

if __name__ == '__main__':
    # 爬取到页面源码数据
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'}
    url = 'http://pic.netbian.com/4kyingshi/'

    response = requests.get(url=url, headers=headers)
    # 手动设定编码
    # response.encoding = 'utf-8'
    page_text = response.text
    # 创建etree实例对象
    tree = etree.HTML(page_text)

    img_src_list = tree.xpath('/html/body/div[@class="wrap clearfix"]/div[@id="main"]/div[@class="slist"]/ul//li')
    if not os.path.exists('./tupian'):
        os.mkdir('./tupian')
    for i in img_src_list:
        img_scr = 'http://pic.netbian.com' + i.xpath('./a/img/@src')[0]
        img_name = i.xpath('./a/img/@alt')[0] + '.jpg'
        # 处理中文乱码较为常用的办法
        img_name = img_name.encode('iso-8859-1').decode('gbk')
        img_data = requests.get(url=img_scr, headers=headers).content
        # 持久化存储
        imgPath = 'tupian/' + img_name
        with open(imgPath, 'wb') as fp:
            fp.write(img_data)
  • 需求:从网站爬取所有城市名(https://www.aqistudy.cn/historydata/)
import requests
from lxml import etree
import os
import json

if __name__ == '__main__':
    # 爬取到页面源码数据
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15'}
    url = 'https://www.aqistudy.cn/historydata/'
    # 爬取整张页面
    page_text = requests.get(url=url, headers=headers).text

    # 创建etree实例对象
    tree = etree.HTML(page_text)
    city_src_list = tree.xpath('/html/body/div[@class="container"]/div[@class="row"]/div[@class="col-lg-9 col-md-8 col-sm-8 col-xs-12"]/div[@class="all"]/div[@class="bottom"]/ul')
    fp = open('city_name.txt', 'w', encoding='utf-8')
    for i in city_src_list:
        city_name = i.xpath('.//li/a//text()')
        for j in city_name:
            fp.write(j + '、')
        fp.write('\n')

你可能感兴趣的:(爬虫)