聚焦爬虫的数据解析

requests

requests:网络请求的模块,模拟浏览器发请求的

- 编码流程:
  - 指定url
  - 发起了请求
  - 获取响应数据
  - 数据解析   
- 持久化存储 安装 pip install requests

概念补充

- 反爬
    - robots
    - UA伪装
        - 请求载体的身份标识 User_Agent
        - 在headers种应用一个字典(请求头信息:UA)
        headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}

- 动态加载的数据
    - 验证我们要爬取的数据,是否为当前页面动态加载出来的?
        - 是:直接爬取不到
        - 不是:可以直接爬取
    - 如何验证页面中的局部数据是否为动态加载呢?
        - 首先在页面中复制一部分的页面内容,然后在通过抓包工具定位到url指定的数据包,在数据包的response中进行刚才复制内容的搜索,搜索到则表示没有动态加载,否则为动态加载!!!
    - 如何处理动态请求参数:
        - 封装到一个字典中,字典需要作用到data或者params

简单get请求实例

#爬取搜狗首页的页面源码数据
import requests
# 1指定url
url = 'https://www.sogou.com/'
# 2发起请求:返回值是一个response
response = requests.get(url=url)
# 3.获取响应数据:text返回的字符串形式的响应数据(页面源码数据)
page_text = response.text
# 4.持久化存储
with open('sogou.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

案例:爬取豆瓣电影排名

import requests

url = "https://movie.douban.com/j/chart/top_list"

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

for start in range(0,200,20):
    data = {
        'type': '11',
        'interval_id': '100:90',
        'action': "",
        'start': start,
        'limit': '20'
    }
    movie_data = requests.post(url=url,data=data,headers=headers).json()
    for movie in movie_data:
        score = movie["rating"][0]
        title = movie["title"]
        num = movie["rank"]
        print("评分:"+score,"名字:"+title, num)

数据解析

  --  是为了实现聚焦爬虫

- 数据解析的作用
    - 用于获取页面中局部的页面源码数据

- 如何实现数据解析
    - 正则
    - bs4(python独有)
    - xpath(最为通用)
    - pyquery

- 数据解析的通用原理是什么?
    - 标签定位
    - 将标签中间存储的文本数据或者其属性值进行捕获

正则

import requests
import os
import re
from urllib import request
headers = {
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3861.0 Safari/537.36 Edg/77.0.230.2"
}

# 封装一个通用的url模板
url = 'https://www.qiushibaike.com/pic/page/%d/'
dirname = './糗事百科图片'
# 如果没有文件夹,新建文件夹
if not os.path.exists(dirname):
    os.mkdir(dirname)
    
for page in range(1,4):
    print("正在爬取第{}页的数据".format(page))
    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可以处理findall中的回车换行
    # src是不完整的,需要拼接
    for img_src in img_src_list:
        img_src = "https:"+img_src
        print(img_src)
        # 把最后的名字当做文件名存储
        img_name = img_src.split("/")[-1]
        # 放在指定文件夹下
        img_path = dirname+'/'+img_name
        request.urlretrieve(img_src,img_path)
        print(img_name,"下载成功!!!")

bs4解析

- 环境的安装:
    - pip install bs4
    - pip install lxml

- bs4解析原理
    - 实例化一个BeautifulSoup的对象,且将即将被解析的页面源码加载到该对象中
    - 使用该对象中的属性或者方法进行标签定位和数据提取

- BeautifulSoup对象的实例化方式:lxml就是一款解析器
    - BeautifulSoup(fp,'lxml'):将本地存储的html文档加载到该对象中
    - BeautifulSoup(page_text,'lxml'):将互联网上获取的html源码加载到该对象中

导入:

from bs4 import BeautifulSoup
soup = BeautifulSoup(fp/page_text,'lxml')

总结

- soup.tagName:返回的就是页面中第一次出现的tagName标签(返回的是一个单数)
    soup.div
   # 直接点标签名,返回的是第一个标签内容

- find函数的用法:属性定位
    - soup.find('tagName',attrName='value')
    soup.find('div',class_='song')
    # 返回的是单数

- soup.find_all('tagName'):定位所有的tagName的标签
- soup.find_all('tagName',attrName='value'):属性定位
    soup.find_all('div')
    # 返回的是列表

- select('选择器'):根据选择器进行标签定位且返回的是复数(列表)
    - 类选择器,id选择器,标签选择器,层级选择器
    - 层级选择器:>表示一个层级,空格表示多个层级
    soup.select('.tang > ul > li')  == soup.select('.tang li')
    
- 取数据(属性值和标签中存储的文本数据)
    soup.p.string
    soup.p.text
    取的是标签中所有的文本内容
    - text和string的区别?
        - string获取的是标签中直系的文本内容
        - text获取的是标签中所有的文本内容

- 取属性:
    - tag['attrName']
    for a in soup.select('.tang > ul > li > a'):
        print(a['href'])

bs4实例(水浒传)

from bs4 import BeautifulSoup

url = "http://www.shicimingju.com/book/shuihuzhuan.html"
page_text = requests.get(url=url,headers=headers).text

# 使用bs4解析数据(标题+内容)
soup = BeautifulSoup(page_text,"lxml")
# 使用层级选择器
a_list = soup.select('.book-mulu > ul > li > a')
fp = open("水浒传.txt","w",encoding='utf-8')
for a in a_list:
    title = a.string
    detail_url = "http://www.shicimingju.com"+a['href']
    # 对详情页的url发起请求
    detail_text = requests.get(url=detail_url,headers=headers).text
    detial_soup = BeautifulSoup(detail_text,"lxml")
    content = detial_soup.find('div',class_="chapter_content").text
    # 保存到文件
    fp.write(title+":"+content+'\n')
    print(title,"保存成功!!!")
fp.close()

xpath解析

- 环境安装:pip install lxml
- 解析原理:
    - 实例化一个etree类型的对象,且将即将被解析的页面源码数据加载到该对象中
    - 调用该对象中的xpath方法结合着不同的xpath表达式进行标签定位和数据提取
- 实例化对象:
    - etree.parse(fileName)
    - etree.HTML(page_text)

导入

from lxml import etree
# from lxml.html.clean import tree

总结

- xpath方法返回值永远是一个列表

- xpath表达式中最左侧的/和//的区别是什么?
    - /表示我们必须从根标签进行定位
    - //表示我们可以从任意位置标签定位

- 属性定位
    tree.xpath('//div[@class="song"]')

- 索引定位
    - 索引值是从1开始

- 在xpath表达式中非最左侧的/和//的区别?
    - /表示一个层级
    - //表示多个层级

- 取文本
    - /text():获取的是标签下直系的文本数据
    - //text():获取的是标签下所有的文本数据

- 取属性
    tree.xpath('//div[@class="tang"]//li[5]/a/@href')

xpath实例(爬boss直聘)

# 通用的url模板
url = "https://www.zhipin.com/c101010100/?query=Java&page=%d"
fp = open("boos直聘.txt","w",encoding="utf-8")
for page in range(1,4):
    print("正在爬取第{}页的数据".format(page))
    new_url = format(url%page)
    page_text = requests.get(url=new_url,headers=headers).text
    # xpath解析
    tree = etree.HTML(page_text) # tree中存储的是整个页面对应的页面源码数据
    li_list = tree.xpath("//div[@class='job-list']/ul/li")
    for li in li_list:
        # li中存储的是局部li标签对应的页面源码数据
        job_title = li.xpath('./div/div[1]/h3/a/div[1]/text()')[0]
        salary = li.xpath('./div/div[1]/h3/a/span/text()')[0]
        detail_url = "https://www.zhipin.com" + li.xpath('./div/div[1]/h3/a/@href')[0]
        company = li.xpath('.//div[@class="company-text"]/h3/a/text()')[0]
        # 详情页发起请求获取岗位描述
        detail_page_text = requests.get(url=detail_url,headers=headers).text
        detail_tree = etree.HTML(detail_page_text)
        job_desc = detail_tree.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()')
        job_desc = "".join(job_desc)
        fp.write(job_title+"-"+salary+"-"+company+"\n"+job_desc+"\n\n\n")
fp.close()

补充:中文乱码解决

img_name = img_name.encode("iso-8859-1").decode("gbk")

补充:管道的使用

tree.xpath('//div[@class="bottom"]/ul/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()')
user = info.xpath( './div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()')[0]

 

posted @ 2019-08-08 14:52  blog_wu  阅读(156)  评论(0)    收藏  举报