爬虫Day3 数据解析bs4,xpath

数据解析

1 聚焦爬虫
爬取页面中指定内容数据

2 数据解析
1.正则表达式
2.bs4
3.xpath(重点)

3 数据解析的原理
解析的数据内容都会在标签之间或者标签对应的属性之间进行存储,那如果定位到指定的标签,再对标签或者标签的属性进行提取(解析)

4 流程
1.指定url
2.UA伪装
3.获得响应数据
4.数据解析
5.持久化存储

5 实战:爬取图片数据
在糗事百科上照一张图,获取地址url

import requests
import os

if __name__ == "__main__":
    
    url = "https://pic.qiushibaike.com/system/pictures/12407/124078483/medium/N32DL358XZ7FRTCT.jpg"
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    img_data = requests.get(url = url).content
    # content为获取二进制,text为获取文本形式,json()为获取json格式
    
    with open("./pic.jpg","wb") as f: # 要二进制写入
        f.write(img_data)

但是这样只能爬取一张一张的爬取图片,需要找到一种批量获取图片网址的方法
在百度上搜索jpg格式的图片
随着鼠标滚动,可以看出有很多的局部数据刷新包,得到响应信息:

    url = "https://image.baidu.com/search/acjson?tn=resultjson_com&logid=9658182917810117021&ipn=rj&ct=201326592&is=&fp=result&queryWord=jpg&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=&copyright=&word=jpg&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1614221263049="
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    

    '''
    从AJAX请求包里提取响应到的信息,从信息中抓取图片网址
    '''
    
    
    response = requests.post(url = url, headers = headers)
    
    text = response.text
    
    # print(response) 

re.match(),re.findall(),re.search()的关系
这里用findall

    '''
    提取图片网址
    '''
    r = re.findall(r'thumbURL":"https://(.*?).jpg',text) # .*贪婪模式  .*?懒惰模式

找到的是http://之后.jpg之前的东西,前后补全,可以构成图片的url
然后一张张存储则可以实现

import requests
import os
import re


if __name__ == "__main__":
    
    url = "https://image.baidu.com/search/acjson?tn=resultjson_com&logid=9658182917810117021&ipn=rj&ct=201326592&is=&fp=result&queryWord=jpg&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=&copyright=&word=jpg&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1614221263049="
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    

    '''
    从AJAX请求包里提取响应到的信息,从信息中抓取图片网址
    '''
    
    
    response = requests.post(url = url, headers = headers)
    
    text = response.text
    
    # print(response) 
    
    
    '''
    提取图片网址
    '''
    r = re.findall(r'thumbURL":"https://(.*?).jpg',text) # .*贪婪模式  .*?懒惰模式
    
    '''
    将找到的图片网址一个个存储下来
    '''
    count = 0
    for i in r:
        pic_url = "https://" + i + ".jpg"
        response = requests.get(url = pic_url, headers = headers)
        response_b = response.content # 图片以二进制存储
        save_path = './pics/' + str(count + 1) + ".jpg"
        with open(save_path,"wb") as f:
            f.write(response_b)
        count += 1
        print(".\pics\%d.jpg saved!" %count)
        

在这里插入图片描述
可以看到存储了一个包里面的三十张图片
在这里插入图片描述
6 bs4实现数据解析
bs4解析数据的原理:对标签进行定位,解析标签或者标签属性中的数据
bs4数据解析流程:
1.实例化一个BeautifulSoup对象,并将页面源码数据加载到该对象当中
2.通过调用BeautifulSoup对象中的相关方法进行标签定位或者数据解析

要首先下载bs4模块,要下载lxml解析器:
pip install bs4
pip install lxml (xpath也会用到)
(有可能会下载失败,下载失败换channel到国内的源就好了)

实例化BeautifulSoup的方法有两个:
1.将本地html文档加载到BeatifulSoup中

fp = open('./test.html','r',encoding = 'utf-8')
soup = BeautifulSoup(fp,'lxml')

2.将互联网上获取的网页源码加载到BeautifulSoup中

page_text = response.text
soup = BeautifulSoup(page_text,'lxml')

通常使用第二种,第一种还要单独存储html文件

soup提供的用于数据解析的方法和属性
1.soup.tagName
返回的是文档中第一次出现的tagName对应的标签
2.soup.find(’tagName‘)
同soup.div,同时也可以实现属性定位soup.find('div',class_ = 'song')
3.soup.find_all('tagName')返回的是符合要求的所有标签,返回的是一个列表
4.soup.select('某种选择器(可以是id,class,标签等选择器)')另外还可以作为层级选择器使用soup.select('.tang > ul > li > a')
5.获取标签之间的文本数据soup.a.text/string/get_text()string只能获取该标签下的直系文本内容,text可以获取标签下所有的文本内容
6.获取标签中的属性值soup.a['href']

7 bs4解析应用到实战中
需求:爬取三国演义小说中所有的章节标题和章节内容
网址:https://www.zggdwx.com/sanguo.html#.E4.BD.9C.E8.80.85

在这里插入图片描述

找到目录在什么地方,在mdui-list类下面,用select定位到mdui-list下面的a标签这里

from bs4 import BeautifulSoup
import requests

if __name__ == '__main__':
    # 首先对首页中的页面数据进行爬取
    headers =  {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    url= 'https://www.zggdwx.com/sanguo.html#.E4.BD.9C.E8.80.85'
    
    page_text = requests.get(url = url,headers = headers).text
    
    # 在首页中解析出章节的标题和详情页的url
    # 使用bs4解析数据
    # 1.实例化一个BeautifulSoup对象
    soup = BeautifulSoup(page_text,'lxml')
    # 2.解析章节标题和详情页的url
    # 这里最好选select选择器定位到所有的a标签
    mulu_list = soup.select('.mdui-list > a')

得到的muli_list是这样的
在这里插入图片描述
从若干a标签里面找到网址和标题数据

    url_list = []
    title_list = []
    for a in mulu_list:
        detail_url = a['href'] # 详情页的url
        title = a.string # 获取每一章的标题
        print(detail_url)
        print(title)
        url_list.append(detail_url)
        title_list.append(title)

在这里插入图片描述
接下来就是对详情页进行分析,找到内容的部分
在这里插入图片描述
那可以看到详情在”mdui-card mdui-card-shadow mdui-typo“这个class下面,这里直接取出这个下面的所有文本,不用select查找p标签了,不然还要一段一段给拼接起来

    # 取其中一个url进行目录的提取,如果要提取所有的可以加一个循环
    detail_text = requests.get(url = url_list[5],headers = headers).text
    detail_soup = BeautifulSoup(detail_text,'lxml')
    content = detail_soup.find('div', class_ = 'mdui-card mdui-card-shadow mdui-typo')
    print(content.text)

在这里插入图片描述

    #存储
    fp = open('./sanguo.txt',"w",encoding = 'utf-8')
    fp.write(content.text)

8 xpath解析
xpath的原理:
1.实例化一个etree对象,将页面源码加载到该对象中
2.调用etree中的xpath方法结合xpath表达式实现标签的定位和内容的捕获

xpath环境需要安装:
lxml

如何实例化etree对象,两种方法:
1.本地html加载到etree对象中etree.parse(filepath)
2.互联网上获取的源码数据加载到对象中etree.HTML('page_text')

xpath表达式的编写:

r = tree.xpath('/html/head/title') #第一个/表示从根节点开始
r = tree.xpath('html//title') #//表示多个层级,相当于bs4的空格
r = tree.xpath('//title') #//是一个意思,可以表示从任何位置开始定位

实现属性定位

tree.path('//div[@class = 'song']')

实现索引定位

tree.xpath('//div[@class = "song"]/p[3]') #索引是从1开始的

获取标签的文本内容和属性的数值

r = tree.path('//div[@class = "tang"]//li[5]/p/text()')[0]

/text()获取标签下直系内容
//text()获取标签下所有内容

r = tree.xpath('//div[@class = "song"]/img/@src')

/@属性名字 取得属性的值

10 xpath实战:爬取58二手房的房源信息
网址:https://bj.58.com/ershoufang/

找到每一个二手房源信息所在的位置
在这里插入图片描述

from lxml import etree
import requests

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'https://bj.58.com/ershoufang/'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批div对象的列表
    div_list = tree.xpath('//section[@class = "list"]/div')

在这里插入图片描述
对每个对象定位到标题和房价信息:

from lxml import etree
import requests

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'https://bj.58.com/ershoufang/'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批div对象的列表
    div_list = tree.xpath('//section[@class = "list"]/div')
    
    # 3.对每个div对象进行xpath解析,得到每个二手房信息的title
    house_dic = {}
    for div in div_list:
        title = div.xpath('./a/div[2]/div[1]/div[1]/h3/text()')[0] #表示当前解析的标签位置开始,即从div开始
        price = div.xpath('./a/div[2]/div[2]/p[1]/span[1]/text()')[0]
        price_text = div.xpath('./a/div[2]/div[2]/p[1]/span[2]/text()')[0]
        house_dic.update({title:str(price) + price_text})
        
    print(house_dic)

在这里插入图片描述
11 xpath爬取图片
网址:http://pic.netbian.com/4kmeinv/

from lxml import etree
import requests

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'http://pic.netbian.com/4kfengjing/'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批li对象的列表
    li_list = tree.xpath('//div[@class = "slist"]/ul/li')
    
    # 3.对每个div对象进行xpath解析,得到每个二手房信息的title
    for li in li_list:
        pic_url = 'http://pic.netbian.com/' + li.xpath('./a/img/@src')[0] # 局部数据解析./
        pic_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 获取图片名称
        print(pic_url)
        print(pic_name)

如片名称出现乱码现象,对乱码的中文部分进行解码

from lxml import etree
import requests

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'http://pic.netbian.com/4kfengjing/'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批li对象的列表
    li_list = tree.xpath('//div[@class = "slist"]/ul/li')
    
    # 3.对每个div对象进行xpath解析,得到每个二手房信息的title
    for li in li_list:
        pic_url = 'http://pic.netbian.com/' + li.xpath('./a/img/@src')[0] # 局部数据解析./
        pic_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 获取图片名称
        # 指定编码格式,通用处理中文乱码的解决方法
        pic_name = pic_name.encode('iso-8859-1').decode('gbk')
        
        print(pic_url)
        print(pic_name)

在这里插入图片描述
存储图片:

from lxml import etree
import requests
import os

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'http://pic.netbian.com/4kfengjing/'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批li对象的列表
    li_list = tree.xpath('//div[@class = "slist"]/ul/li')
    
    # 3.对每个div对象进行xpath解析
    if not os.path.exists('./picLibs'):
        os.mkdir('./picLibs')
        
    for li in li_list:
        pic_url = 'http://pic.netbian.com/' + li.xpath('./a/img/@src')[0] # 局部数据解析./
        pic_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 获取图片名称
        # 指定编码格式,通用处理中文乱码的解决方法
        pic_name = pic_name.encode('iso-8859-1').decode('gbk')
        
        pic_data = requests.get(url = pic_url,headers = headers).content
        pic_path = 'picLibs/' + pic_name
        
        with open(pic_path,"wb") as f:
            f.write(pic_data)
            print(pic_name + 'download finish!')

在这里插入图片描述
注意:xpath表达式可以用或者连接:
tree.xpath(’ … | … ')使得xpath表达式具备更强的通用性

12 爬取免费简历模板
网址:http://www.gerenjianli.com/moban/index.html
从网站下批量下载简历
1.先得到单个ppt的下载页面:

from lxml import etree
import requests
import os

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'http://www.gerenjianli.com/moban/index.html'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批li对象的列表
    li_list = tree.xpath('//div[@class = "list_boby"]/ul/li')
    
    # 3.对每一个li对象解析
    for li in li_list:
        ppt_url = li.xpath('./div/span/a/@href')[0]
        ppt_name = li.xpath('./div/span/a/text()')[0]
        # 中文乱码了
        # 指定编码格式,通用处理中文乱码的解决方法
        ppt_name = ppt_name.encode('iso-8859-1').decode('gbk')
        
        print(ppt_url)
        print(ppt_name)    

在这里插入图片描述
然后通过观察找到了点下载按钮触发的是哪个网址
在这里插入图片描述
这个网址一点就触发下载操作,用代码去伪装浏览器触发这个网址的访问:

from lxml import etree
import requests
import os

if __name__ == "__main__":
    
    # 先爬取源码数据
    url = 'http://www.gerenjianli.com/moban/index.html'
    
    headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
    
    page_text = requests.get(url = url, headers = headers).text
    
    # 1.实例化一个etree对象
    tree = etree.HTML(page_text)
    
    # 2.得到一批li对象的列表
    li_list = tree.xpath('//div[@class = "list_boby"]/ul/li')
    
    # 3.对每一个li对象解析
    if not os.path.exists('./pptLib'):
        os.mkdir('./pptLib')
    for li in li_list:
        ppt_url = li.xpath('./div/span/a/@href')[0]
        ppt_name = li.xpath('./div/span/a/text()')[0]
        # 中文乱码了
        # 指定编码格式,通用处理中文乱码的解决方法
        ppt_name = ppt_name.encode('iso-8859-1').decode('gbk')
        
        print(ppt_url)
        print(ppt_name)
        
        ppt_data = requests.get(url = ppt_url, headers = headers).content # 二进制存储
        save_path = './pptLib/' + ppt_name + ".doc"
        
        with open(save_path, "wb") as f:
            f.write(ppt_data)
            print("save_path was download!!!")

在这里插入图片描述
这样就下载好一页里面的所有ppt了,一共36个,那如果想要批量爬取很多页的ppt,需要观察每一个的网址特点,发现只是在index_后面加上一个页数就可以了,第一页没有页码,需要分别处理:

# -*- coding: utf-8 -*-
"""
Created on Mon Mar  1 14:13:12 2021

@author: zhongxi
"""
from lxml import etree
import requests
import os

if __name__ == "__main__":
    
    
    if not os.path.exists('./pptLib'):
        os.mkdir('./pptLib')
        
    url_list = ['http://www.gerenjianli.com/moban/index.html'] # 第一页的网址没有index后面的数字,单独列出来
    for page in range(10):
        url_list.append('http://www.gerenjianli.com/moban/index_' + str(page + 2) + '.html')
        
        
        
    for url in url_list:
       
        headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
        
        page_text = requests.get(url = url, headers = headers).text
        
        # 1.实例化一个etree对象
        tree = etree.HTML(page_text)
        
        # 2.得到一批li对象的列表
        li_list = tree.xpath('//div[@class = "list_boby"]/ul/li')
    
    
        
        for li in li_list:
            try:
                ppt_url = li.xpath('./div/span/a/@href')[0]
                ppt_name = li.xpath('./div/span/a/text()')[0]
                # 中文乱码了
                # 指定编码格式,通用处理中文乱码的解决方法
                ppt_name = ppt_name.encode('iso-8859-1').decode('gbk')
                
                # print(ppt_url) # 得到单个ppt的详情页
                # print(ppt_name)
                
                # 从单个ppt详情页上获取下载链接
                ppt_page_data = requests.get(url = ppt_url, headers = headers).text
                tree = etree.HTML(ppt_page_data)
                download_url = tree.xpath('//div[@class = "donwurl2"]/a/@href')[0]
                print(download_url)
                
                # 获得ppt数据
                ppt_data = requests.get(url = download_url, headers = headers).content # 二进制存储
                save_path = './pptLib/' + ppt_name + ".doc"
                
                with open(save_path, "wb") as f:
                    f.write(ppt_data)
                    print(save_path,"was download!!!")
            except:
                pass
            continue

    

在这里插入图片描述
一共321个,9页数据一共应该是324个,但是可能存在三个ppt名称出现/错误,导致不能下载

这里一定要注意,最后获取的ppt数据一定是从下载链接得到的响应数据,我先开始写成了ppt详情页数据,然后将整张网页存储了下来,ppt是一张图片不能编辑,这是不符合需求的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值