31.爬虫一

request模块:

- request模块的基本使用
    - python中封装好的一个基于网络亲求的模块
- requests模块的作用
    - 用来模拟浏览器发送请求
- requests的环境安装
    - pip install request
- request模块的编码流程:
    - 1指定url
    - 2发起请求
    - 3获取相应数据
    - 4持久化存储
# 爬取搜狗首页页面的源码数据
import requests
# 1.指定url
url = ‘https://www.sogou.com‘
# 2.发起请求
response = requests.get(url=url)
# 3.获取响应对象
page_text = response.text
# page_text
# with open(‘sogou.html‘, "w", encoding=‘utf-8‘) as f:
#     f.write(page_text)
# 实现一个建议的网页采集器
# 需要让url携带的参数动态化
url = ‘https://www.sogou.com/web/‘
# 实现参数动态化
wd = input("enter a key:")
params = {
    "query": wd,
}
# 在请求中需要将请求参数对应的字典作用到params这个get方法的参数中
page_txt = requests.get(url,params=params).text

page_txt

文件乱码:

- 上面带请求数据出现错误
    - 文件乱码
    - 没有拿到数据
    
# 实现一个建议的网页采集器
# 需要让url携带的参数动态化
url = ‘https://www.sogou.com/web/‘
# 实现参数动态化
wd = input("enter a key:")
params = {
    "query": wd,
}
# 在请求中需要将请求参数对应的字典作用到params这个get方法的参数中
response = requests.get(url,params=params)
response.encoding = ‘utf-8‘
page_txt=response.text
# page_txt

反爬机制:

- UA检测:检测到request发送的请求载体的身份表示不是浏览器
- Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36

# 结局UA检测
# 实现一个建议的网页采集器
# 需要让url携带的参数动态化
import requests
url = ‘https://www.sogou.com/web‘
# 实现参数动态化
wd = input("enter a key:")
params = {
    "query": wd,
}
headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"
}
# 在请求中需要将请求参数对应的字典作用到params这个get方法的参数中
response = requests.get(url=url,params=params,headers=headers)
response.encoding = ‘utf-8‘
page_txt=response.text
# page_txt

动态加载数据爬取

# 爬取的是豆瓣电影的详情数据
# 当滚轮话单下面的时候动态加载数据
# 动态加载的数据
    # 通过另一单独请求拿到的
# url = "https://movie.douban.com/explore#!type=movie&tag=%E7%88%B1%E6%83%85&sort=recommend&page_limit=20&page_start=0"
import requests
# url = "https://movie.douban.com/j/chart/top_list"
url = "https://movie.douban.com/j/search_subjects"

headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"
}
start = input("请输入开始页:")
end = input("请输入结束页:")
# dic = {
#     "type": "13",
#     "interval_id": "100:90",
#     "action": "",
#     "start": start,
#     "limit": end,
# }
dic = {
    "type":"movie",
    "tag":"爱情",
    "sort":"recommend",
    "page_limit":start,
    "page_start":end,
}
response = requests.get(url=url,params=dic,headers=headers)
data = response.json() # 返回json格式的数据对象
data
# for item in data["subjects"]:
#     print(item["title"] + item["rate"] + str(len(data["subjects"])))
# 肯德基餐厅查询数据获取
import requests
url = "http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword"
headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"
}
data = {
    "cname": "",
    "pid": "",
    "keyword": "北京",
    "pageIndex":"1",
    "pageSize": "10",
}
response = requests.post(url=url,headers=headers,data=data)
data = response.json()
# data
- 需求分析
    - 爬取药监总局中相关企业的详情信息
- 如何检测页面中是否存在动态加载的数据?
    - 基于抓包工具
        - 先捕获网站请求后的所有的数据包
        - 在数据包中定位到地址栏所对应请求的数据包,在response选项卡对应的数据中进行局部搜索(页面中的某一组内容)
          - 可以搜索到:爬取道的不是动态加载的
          - 搜索不到:爬取到的数据是动态加载的
    - 如何进行动态加载数据在那个数据包中?
        - 进行全局搜索
# 页面数据接口获取
- 需求
    - 爬取药监局总局中相关企业的http://125.35.6.84:81/zk/
- 需求分析
    - 指定页面中企业相关数据是否为动态加载?
        - 相关企业信息是动态加载出来的
    - 通过抓包工具全局搜索,定位动态数据加载。
        - post:http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList
        - 请求返回的响应数据是一组json串,通过对json串的简单解析,没有找到企业详情页的url,但是找到每一个加企业的id
    - 每一家企业详情页的url,域名都是一样的,只有请求参数id值不同
        - 可以使用同一个域名结合这不同的id值拼接成一家完整企业详情页url
        - 判断企业详情页中的数据是否为动态加载?
            - 通过抓包工具检测,发现企业详情信息在详情页中为动态加载
            - 通过抓包工具进行全局搜索,找到数据对应的url
                - url:post:http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById
                    - 请求参数:id: b4437b636b5944eb9eadc1418f312b19
                - 请求到的json串就是我们最终想要的企业详细数据
import requests
headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"
}
url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
data1 = {
    "on": "true",
    "page": "1",
    "pageSize": "15",
    "productName":"",
    "conditionType": "1",
    "applyname":"",
    "applysn":"",
}

response = requests.post(url=url,headers=headers,data=data1)
data = response.json()
for item in data["list"]:
    
    url2 = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById"
    data2 = {
        "id": item["ID"]
    }
    response2 = requests.post(url=url2, headers=headers,data=data2).json()
#     print(response2["businessPerson"]+ response2["legalPerson"])

回顾:

- requests作用:模拟浏览器发起请求
- urllib被requests替代了
- request模块的编码流程:
    - 指定url
    - 发起请求
        - get(url,params,headers)
        - post(url, headers, data)
    - 获取响应数据
    - 持久化存储
- 参数动态化
    - 有些情况下我们是需要将请求参数进行更改,将get或者post请求对应的请求参数封装到一个字典(键值对)中,然后将字典作用到get方法的params参数中或者作用到post发放的data参数中
- UA检测(反爬机制)
    - 什么是UA:请求载体的身份表示,服务器端会检测请求的UA来鉴定身份
    - 反爬机制:UA伪装,通过抓包工具捕获某一浏览器的UA值,封装到字典中,且将该字典作用到headers参数中
- 动态加载数据
    - 通过另一个单独的请求到的数据
- 如果我们要对一个陌生的网站进行指定数据的爬取?
    - 首先要确定爬取的数据在该网站中是否为动态加载的
        - 是:通过抓包工具实现全局搜索,定位动态家在数据对应的数据包,从数据包中提取请求的url和请求参数
        - 不是:通过抓包工具对请求中的所有url响应进行搜索

正则,xpath,bs4:

### 今日内容
- 数据解析
    - 数据解析的作用
        - 可以帮助我们实现聚焦爬虫
    - 数据解析的实现方式
        - 正则
        - bs4
        - xpath(通用)
        - pyquery
    - 数据解析的通用原理
        - 问题1:聚焦爬虫爬取的数据是存在哪里?
            - 都被存储在标签之中和相关标签的属性中
        - 1.定位标签
        - 2.取文本后者属性
import requests
headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36",
}
# 如何爬取图片
url = "https://pic.qiushibaike.com/system/pictures/12296/122968969/medium/LSG56EWMA84YO922.jpg"
img_data = requests.get(url=url, headers=headers).content
# with open("./img.jpg","wb") as f:
#     f.write(img_data)

# 方式二
# 不可以使用UA伪装
from urllib import request
url = "https://pic.qiushibaike.com/system/pictures/12296/122968969/medium/LSG56EWMA84YO922.jpg"
# request.urlretrieve(url,filename="./qiushi.jpg")

正则解析:

<a class="recmd-left multi" href="/article/122969075" rel="nofollow" target="_blank" onclick="_hmt.push([‘_trackEvent‘,‘web-list-multi‘,‘chick‘])">

<img src="//qiubai-video-web.qiushibaike.com/article/gif/RP2LJ8DOWG2TVP7T.jpg?imageView2/1/w/150/h/112" alt="一直给女友吹嘘我厨艺">

<div class="recmd-tag">1图</div>
</a>
ex = ‘<a class="recmd-left.*?<img src="(.*?)" alt=.*?</a>‘
# 正则(麻烦)
# 糗事百科图片爬取1-3页所有的图片
# 设置一个通用的url模板(不可变)
import re
import os
if not os.path.exists("./imgLibs"):
    os.mkdir(‘imgLibs‘)
url = "https://www.qiushibaike.com/8hr/page/%d/"
ex = ‘<a class="recmd-left.*?<img src="(.*?)\?.*?" alt=.*?</a>‘
for i in range(1,4):
    new_url = format(url%i)
    img_data = requests.get(url=new_url, headers=headers).text
    img_list = re.findall(ex, img_data,re.S)
    for src in img_list:
        src = "https:" + src
        img_name = src.split("/")[-1]
        img_path = "imgLibs/" + img_name
        request.urlretrieve(src, img_path)

b64:

- bs4解析
    - bs4解析的原理
        - 实例化一个Beautifulsoup的对象,需要将即将被解析的页面源码数据加载到该对象中
        - 调用BeautifulSoup对象中的想过方法和属性进行标签定位和数据提取
    - 环境安装
        - pip install bs4
        - pip install lxml
    - BeautifulSoup的实例化
        - BeautifulSoup(fp,‘lxml‘),将背地存储的一个html文档中的数据加载到实例化好的BeautifulSoup对象中
        - BeautifulSoup(page_text, "lxml"),将从互联网上获取到的页面源码数据加载到实例化好的BeautifulSoup对象中
        
- 定位标签的操作
    - soup.tagName:定位第一个出现的tagName标签
    - 属性定位:soup.find("tagName", attrName=‘value‘)
    - 属性定位:soup.find_all("tagName", attrName=‘value‘) # 返回列表
    - 选择器定位:soup.select(‘选择器‘) # 返回一个列表
        - 层级选择器:>表示一个层级,空格代表多个层级
- 取文本
    - .sting:只可以获取直系的文本内容
    - .text:
- 去属性
    - [‘属性名称‘]
from bs4 import BeautifulSoup
fp = open("./test.html", "r", encoding="utf-8")
soup = BeautifulSoup(fp, "lxml")
soup.div
soup.find("div", class_="song")
soup.find("a", id="feng" )
soup.find_all("div", class_="song")

soup.select(‘.song‘)
soup.select(‘.tang > ul > li‘)
soup.select(‘.tang li‘)

a_tag = soup.select(‘#feng‘)[0]
a_tag.text

div = soup.div
div.string

div = soup.find(‘div‘,class_=‘song‘)
div.string

a_tag = soup.select(‘#feng‘)[0]
a_tag["href"]
# 爬取三国整片内容 
from bs4 import BeautifulSoup
fp = open("./sanguo.txt",‘w‘,encoding="utf-8")
page_url = "http://www.shicimingju.com/book/sanguoyanyi.html"
page_text = requests.get(url=page_url, headers=headers).text
soup = BeautifulSoup(page_text, "lxml")
a_list = soup.select(‘.book-mulu > ul > li > a‘) # 返回的列表中存储的是一个个li标签
for a in a_list:
    title = a.string
    detail_url = "http://www.shicimingju.com" + a[‘href‘]
    detail_page_text = requests.get(url=detail_url, headers=headers).text
    # 解析详情页中的章节内容
    soup1 = BeautifulSoup(detail_page_text, "lxml")
    content = soup1.find(‘div‘, class_=‘chapter_content‘).text
#     fp.write(title + ":" + content + "\n")
#     print(title, "下载成功")
# # print("over")

xpath:

- xpath解析
    - xpath解析实现的原理
        - 1.实例化一个etree对象,然后将即将被解析的页面源码加载到该对象中
        - 2.使用etree对象中xpath方法结合这不同形式的xpath表达式实现标签定位和数据提取
    - 环境安装
        pip install lxml
    - etree对象实例化
        - etree.parse(‘test.html‘)
        - etree.HTML(page_text)
        
- xpath表达式
    - 最左侧的/表示:xpath表达式一定要从跟标签逐层进行查找和定位
    - 最左侧//表示:xpath表达式可以从任意位置定位标签
    - 非最左侧的/:表示一个层级
    - 非最左侧//表示:表示多个层级
    - 属性定位://tagName[@attrName="value"]
    - 索引定位:
- 取文本:
    - /text() : 直系文本
    - //text() : 所有的文本内容
- 取属性
    - /@attrName
from lxml import etree
tree = etree.parse(‘./test.html‘)
tree.xpath(‘/html/head/title‘)
tree.xpath(‘//title‘)
tree.xpath(‘//dic[@class="song"]‘)
tree.xpath(‘//li[2]/text()‘)
tree.xpath("//li[2]/a/@href")
# 爬取糗事百科段子内的作者名称
import requests
from lxml import etree
url = "https://www.qiushibaike.com/text/"
page_text = requests.get(url=url, headers=headers).text
# 解析内容
tree = etree.HTML(page_text)
div_list = tree.xpath("//div[@class=‘col1 old-style-col1‘]/div")
for div in div_list:
    author = div.xpath(‘./div[1]/a[2]/h2/text()‘)[0]
    content = div.xpath(‘./a[1]/div/span/text()‘)
    content = "".join(content)
#     print(author,content)

合并条件:

# 合并xpath表达式,提高xpath的通用性
a.xpath("./b/text() | ./img/@src")

错误分析

- HttpConnectionPoll
    - 原因:
        - 短时间内发起了太多请求
        - http连接池中的连接资源被耗尽
    - 解决:
        - 使用代理
        - headers中加入Conection:"close"

相关推荐