2-爬虫-数据解析之bs4与xpath、cookie反爬、代理反爬

数据解析

  • 正则(几乎不用)
  • bs4
  • xpath(***)
  • pyquery(自学)

在爬虫中为什么需要使用数据解析?

  • 数据解析作用:帮助我们可以实现聚焦爬虫(爬取局部数据)
  • 聚焦爬虫实现流程:
    • 1.指定url
    • 2.发起请求
    • 3.获取响应数据
    • 4.数据解析
    • 5.持久化存储
  • 数据解析通用原理
    • 1.html作用?
      • html主要的作用就是用来展示数据
    • 2.我们要解析的数据其实都是存储在
      • html的标签之中
      • 标签的属性
  • 通用原理:
    • 定位标签
    • 获取标签中的数据或者标签的属性值
  • 环境安装:pip install bs4,pip install lxml
  • bs4解析原理
    • 实例化一个BeautifulSoup的对象,且将被解析的页面数据加载到该对象中
    • 调用BeautifulSoup对象中相关的方法和属性进行标签定位和相关数据的提取
  • BeautifulSoup的对象实例化方式:
    • 方式1:BeautifulSoup(fp,'lxml')
      • 可以将本地存储的html文件中的数据进行数据解析
    • 方式2:BeautifulSoup(page_text,'lxml')
      • 可以将互联网上请求到的页面数据进行数据解析
  • 标签定位:
    • soup.tagName:可以定位到第一次出现的该标签
    • 属性定位:
      • soup.find('tagName',attrName='attrValue')
        • 注意:find只可以定位到第一次出现的该标签
      • soup.find_all('tagName',attrName='attrValue')
        • 注意:find_all可以定位到符合条件所有的标签,返回值一定是列表
    • 选择器定位:select('选择器')
      • id,类,层级选择器
      • 层级选择器:
        • 大于号:表示一个层级
        • 空格:表示多个层级
  • 取文本
    • tag.string:获取标签直系的文本内容
    • tag.text:获取标签下所有的文本内容
  • 取属性
    • tag['attrName']
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_all('div',class_='song')
soup.find_all('a',id='feng')
soup.select('#feng')
soup.select('.song')
soup.select('.tang > ul > li > a')
soup.select('.tang a')
 

[<a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a>,
 <a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a>,
 <a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a>,
 <a class="du" href="http://www.sina.com">杜甫</a>,
 <a class="du" href="http://www.dudu.com">杜牧</a>,
 <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>]
 

soup.find('a',id="feng").text
 

'凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘'
 

soup.find('div',class_='song').text
 

'\n李清照\n王安石\n苏轼\n柳宗元\n\nthis is span\n\t\t宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱\n总为浮云能蔽日,长安不见使人愁\n\n'
 

soup.find('a',id="feng")['href']
 

'http://www.haha.com'
import requests
from bs4 import BeautifulSoup
headers = {
    'User-Agent':'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'
}
fp = open('sanguo.txt','w',encoding='utf-8')
url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
page_text = requests.get(url=url,headers=headers).text
#数据解析:章节标题+详情页的url
soup = BeautifulSoup(page_text,'lxml')
a_list = soup.select('.book-mulu > ul > li > a')
for a in a_list:
    title = a.string
    detail_url = 'http://www.shicimingju.com'+a['href']
    #对详情页的url发起请求获取详情页的页面源码数据
    page_text_detail = requests.get(url=detail_url,headers=headers).text
    #数据解析:章节内容
    detail_soup = BeautifulSoup(page_text_detail,'lxml')
    content = detail_soup.find('div',class_='chapter_content').text
    fp.write(title+':'+content)
    print(title,'下载保存成功!!!')
fp.close()

xpath解析 

    • 环境安装:pip install lxml

    • xpath解析的原理:

      • html标签是基于树状的结构存在的

      • 1.实例化一个etree的对象,且将被解析的数据加载到该对象

      • 2.调用etree对象中的xpath方法结合着不同形式的xpath表达式进行标签定位和数据提取

    • etree对象的实例化两种方式:

      • 方式1:etree.parse('filename')

      • 方式2:etree.HTML(page_text)

  • 标签定位

    • 最左侧的/:一定要从根标签开始进行指定标签的定位

    • 非最左侧的/:表示一个层级

    • 非最左侧的//:表示多个层级

    • 最左侧的//:可以从任意位置定位指定标签(常用)

    • 属性定位://tagName[@attrName="attrValue"]

    • 索引定位://tagName[index],注意索引是从1开始

    • 模糊匹配:

      • //div[contains(@class, "ng")]:定位到class属性值中包含ng的div标签

      • //div[starts-with(@class, "ta")]:定位到class属性值是以ta开头的标签

  • 取文本

    • /text():可以将标签中直系的文本内容取出,且返回的列表元素只有一个

    • //text():可以将标签中所有的文本内容取出,且返回的列表元素为多个

  • 取属性

    • /@attrName

from lxml import etree
tree = etree.parse('test.html')
tree
<lxml.etree._ElementTree at 0x11387ad48>
#定位title标签
tree.xpath('/html/head/title')
tree.xpath('/html//title')
tree.xpath('//title')
[<Element html at 0x113db1ec8>]
#定位class属性值为cong的div标签
tree.xpath('//div[@class="song"]')
[<Element div at 0x113fa00c8>]
tree.xpath('//a[@id="feng"]/text()')[0]
'凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘'
''.join(tree.xpath('//div[2]//text()'))
'\n\t\t李清照\n\t\t王安石\n\t\t苏轼\n\t\t柳宗元\n\t\t\n\t\t\tthis is span\n\t\t宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱\n\t\t总为浮云能蔽日,长安不见使人愁\n\t\t\n\t'
tree.xpath('//a[@id="feng"]/@href')[0]
'http://www.haha.com'
  • 爬取糗事百科的段子内容

url_model = 'https://www.qiushibaike.com/text/page/%d/'
for page in range(1,14):
    url = format(url_model%page)
    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:
        #局部的数据解析
            #每次循环使用的div表示就是页面中定位到的指定div标签(包含了要解析的内容)
        #注意:./就是xpath方法的调用者
        author = div.xpath('./div[1]/a[2]/h2/text()')[0] #想要从div表示的指定标签数据中进行指定数据的解析
        content = div.xpath('./a[1]/div/span//text()')
        content = ''.join(content)
        print(author,content)
  • 拓展:

    • 可以将文字数据进行语音合成

      • 可以使用百度AI进行语音合成

 

 

 

 

import os
dirName = 'GirlsLib'
if not os.path.exists(dirName):
    os.mkdir(dirName)
url_model = 'http://pic.netbian.com/4kmeinv/index_%d.html'
for page in range(1,6):
    if page == 1:
        url = 'http://pic.netbian.com/4kmeinv/'
    else:
        url = format(url_model%page)
    response = requests.get(url=url,headers=headers)
    response.encoding = 'gbk'
    page_text = response.text
    #数据解析:图片的名称+图片的地址
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li')
    for li in li_list:
        title = li.xpath('./a/img/@alt')[0]+'.jpg'
        img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
        #对图片地址发起请求
        img_data = requests.get(url=img_src,headers=headers).content
        img_path = dirName+'/'+title
        with open(img_path,'wb') as fp:
            fp.write(img_data)
        print(title,'写入成功!')
  • 想要解析出携带标签的文本内容,如何实现?

    • 使用bs4

 

cookie反爬

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

url = 'https://xueqiu.com/statuses/hot/listV2.json?since_id=-1&max_id=72813&size=15'
requests.get(url,headers=headers).json()
#问题:没有获取数据,原因一定是没有严格模拟浏览器上网的流程
  • 在爬虫中处理cookie的方式:

    • 手动处理

      • 从抓包工具中将cookie封装到headers字典中

        • 局限性:如果cookie过了有效时长,则该种方式会失效

    • 自动处理

      • 使用session机制。

      • 1.可以获取一个session对象

      • 2.可以基于该对象进行请求的发送

        • 在请求发送的过程中,如果产生了cookie,则cookie会被存储到该对象中。

        • 如果cookie被存储到了session对象中,则再次使用session对象发请求,则该次请求就是携带者cookie进行的请求发送

      • 注意:Session对象在使用的时候,至少需要被调用两次。

sess = requests.Session() #创建了一个sessio对象
first_url = 'https://xueqiu.com/'
#该次请求会产生cookie,且cookie存储到了session对象中
sess.get(url=first_url,headers=headers)
​
url = 'https://xueqiu.com/statuses/hot/listV2.json?since_id=-1&max_id=72813&size=15'
#该次请求就是携带cookie进行的请求发送
sess.get(url,headers=headers).json()  

代理反爬

  • 代理反爬

    • 什么是代理

      • 就是代理服务器

    • 代理服务器的作用:

      • 可以进行请求和响应的转发/拦截

    • 在爬虫中为何需要使用代理?

      • 如果我们使用爬虫对一个网站在段时间内发起一个高频的请求,该网站会检测出这个异常的现象,且会将异常的请求ip获取,将ip加入到黑名单,然后该ip在近期就无法再次对该网站进行网络访问。

      • 如果本机ip被对方服务器加入到了黑名单,则我们就可以使用代理服务器进行请求转发,最终对方服务器获取的请求ip就是代理服务器的不在我们自己本机的。

    • 代理的匿名度:

      • 透明:对方服务器知道你使用了代理,也知道你的真实ip

      • 匿名:对方服务器知道你使用了代理,但是不知道你的真是ip

      • 高匿:不知道使用了代理,也不知道你的真实ip

    • 代理的类型

      • http:只能转发http协议的请求

      • https:只能转发https协议的请求

from lxml import etree
#没有使用代理的情况
url = 'https://www.sogou.com/web?query=ip'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
tree.xpath('//*[@id="ipsearchresult"]//text()')
#使用代理的情况
url = 'https://www.sogou.com/web?query=ip'
page_text = requests.get(url=url,headers=headers,proxies={'https':'114.239.119.157:42559'}).text
tree = etree.HTML(page_text)
tree.xpath('//*[@id="ipsearchresult"]//text()')
  • 需求:

    • 对一个网站发起高频请求,然后让其将本机ip加入黑名单,构建代理池处理

import random
​
#提取代理精灵代理服务器ip+port的方法
all_proxy = [] #代理池
url = 'http://ip.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=51&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
data_list = tree.xpath('//body//text()')
for data in data_list:
    dic = {}
    dic['https'] = data
    all_proxy.append(dic)
url_model = 'http://www.521609.com/daxuemeinv/list8%d.html'
all_data = []
for page in range(1,23):
    url = format(url_model%page)
    page_text = requests.get(url=url,headers=headers,proxies=random.choice(all_proxy)).text
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
    for li in li_list:
        detail_url = 'http://www.521609.com'+li.xpath('./a[1]/@href')[0]
        page_text_detail = requests.get(detail_url,headers=headers).text
        all_data.append(page_text_detail)
print(len(all_data))

作业

posted @ 2020-07-04 13:43  电竞杰森斯坦森  阅读(388)  评论(0)    收藏  举报