python学习——xpath

Posted bilx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python学习——xpath相关的知识,希望对你有一定的参考价值。

回顾

  • 5种反爬机制
    • robots.txt:反爬机制,防君子不防小人
    • UA检测:UA伪装
    • 数据加密
    • 图片懒加载
    • 代理ip
  • requests模块爬取流程:
    • 指定url
    • 发起请求
    • 获取页面数据
    • 数据解析
    • 持久化存储
  • bs4解析:
    • 环境安装:bs4、lxml解析器
    • 实例化bs对象,将页面源码数据加载到该对象中
    • 定位标签
      • find(‘a‘,class_=‘xxx‘)
      • findall()
      • select()
        • 大于号,一个层级
        • 空格,多个层级
    • 将标签中的文本内容获取
      • string 返回标签下文本内容
      • text 返回标签下所有字标签问本内容
      • get_text()
      • 获取属性
        • a[‘href‘]
 

xpath使用(【重点】xpath表达式)

  • 环境安装:pip install lxml
  • 解析原理:
    • 获取页面源码数据
    • 实例化一个etree的对象,并且将页码源数据加载到该对象中
    • 调用该对象的xpath方法进行制定标签的定位
    • 【注意】xpath函数必须结合着xpath表达式进行标签定位和内容捕获
  • html文档或xml文档转换成一个etree对象,然后调用对象中的方法查找指定的节点
    • 本地文件:tree = etree.parse(文件名)
         tree.xpath(‘xpath表达式‘)
    • 网络数据:tree = etree.HTML(网页内容字符串)
         tree.xpath(‘xpath表达式‘)
 

xpath表达式

  • / 层级之间的关系
    • / 相当于bs4中select中的>
    • // 相当于bs4-select中的空格
  • 举例:
    • /html/head/title 从根目录开始找,html下的 head标签下的 title标签
    • //head/title 先找到当前源码中所有的head标签,在找到head标签下的title标签
    • //title 找到所有title标签
    • 属性定位:
      • //div[@class=‘song‘] 定位所有class属性值为song的div标签;[]中必须跟@符号,属性名称前必须有@【语法结构】,返回的是列表
    • 层级&索引定位:
      • //div[@class=‘tang‘]/ul/li[2]/a 定位所有class属性值为tong的div直系标签 ul标签下的 第二个li标签下的直系字标签 a标签;
    • 逻辑运算:
      • //a[@href=‘‘ and @class=‘du‘] 定位所有href属性值为空且class属性值为du的所有a标签
    • 模糊匹配:
      • //div[contain(@class,‘ng‘)] 定位class属性值包含ng的所有div标签
      • //div[start-with(@class,‘ta)] 定位class属性值以ta开头的所有div标签
    • 取文本
      • 表示获取某个标签下的文本内容
      • 表示获取某个标签下的文本内容和所有子标签下的文本内容
      • //div[@class=‘song‘]/p[1]/text() 获取class属性值为song的所有div标签下的 第一个p字标签 包含的文本
      • //div[@class=‘tang]//text() 获取class属性值为tang的所有div标签下的 所有文本,及其字标签下的所有文本,返回的是列表,列表里有多个列表元素
    • 取属性
      • //div[@class=‘tang‘]//li[2]/a/@href 返回属性对应的属性值
 

案例:获取58二手房相关房源信息

In [1]:
import requests
from lxml import etree

url = ‘https://bj.58.com/beijingzhoubian/ershoufang/?PGTID=0d30000c-0000-1175-8e33-a6e941f8aff5&ClickID=1‘
headers = 
    ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘

# 获取源码数据
page_text = requests.get(url=url,headers=headers).text
# 实例化etree对象
tree = etree.HTML(page_text)
# 调用xpath方法,后去li标签列表
li_list = tree.xpath(‘//ul[@class="house-list-wrap"]/li‘)
fp = open(‘58.csv‘,‘w‘,encoding=‘utf-8‘)
#遍历列表
for li in li_list:
    # .开头的意思:进行局部页面解析;./开头表示从li标签开始解析
    title = li.xpath(‘./div[2]/h2/a/text()‘)[0]
    price = li.xpath(‘./div[3]//text()‘)
    #将价格的三个列表拼接为字符串
    price = ‘‘.join(price)
    fp.write(title+‘:‘+price+\n)
fp.close()
print(‘over‘)
 
over
 

案例:获取图片

In [27]:
import requests
from lxml import etree
import os
import urllib

url = ‘http://pic.netbian.com/4kmeinv/‘
headers = 
    ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘

response = requests.get(url=url,headers=headers)
#response.encoding = ‘utf-8‘
if not os.path.exists(‘./imgs‘):
    os.mkdir(‘./imgs‘)
page_text = response.text

tree = etree.HTML(page_text)
li_list = tree.xpath(‘//dic[@class="slist"]/ul/li‘) # //dic[@class="slist"]//li
for li in li_list:
    img_name = li.xpath(‘./a/b/text()‘)[0]
    # 处理中文乱码
    img_name = img_name.encode(‘iso-8859-1‘).decode(‘gbk‘)
    img_url = ‘http://pic.netbian.com‘ + li.xpath(‘./a/img/@src‘)[0]
    img_path = ‘./imgs/‘ + img_name + ‘.jpg‘
    urllib.request.urlretrieve(url=img_url,filename=img_path)
    print(img_path,‘下载成功‘)
 

案例:煎蛋网中图片数据:http://jandan.net/ooxx

  • 第三种反爬机制:数据加密
In [6]:
import requests
from lxml import etree
import base64
import urllib

headers = 
    ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘

url = ‘http://jandan.net/ooxx‘
page_text = requests.get(url=url,headers=headers).text

tree = etree.HTML(page_text)
img_hash_list = tree.xpath(‘//span[@class="img-hash"]/text()‘)
for img_hash in img_hash_list:
    img_url = ‘http:‘ + base64.b64decode(img_hash).decode()
    img_name = img_url.split(‘/‘)[-1]
    urllib.request.urlretrieve(url=img_url,filename=img_name)
 

爬取站长素材中的简历模板

In [12]:
import requests
from lxml import etree
import random

headers = 
    ‘Connection‘:‘close‘,# 当请求成功后,马上断开该次请求(及时释放请求池中的资源))
    ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘

url = ‘http://sc.chinaz.com/jianli/free-%d.html‘
for page in range(1,4):
    if page == 1:
        new_url = ‘http://sc.chinaz.com/jianli/free.html‘
    else:
        new_url = format(url%page)
    response = requests.get(url=new_url,headers=headers)
    response.encoding = ‘utf-8‘
    page_text = response.text

    tree = etree.HTML(page_text)
    div_list = tree.xpath(‘//div[@id="container"]/div‘)
    for div in div_list:
        detail_url = div.xpath(‘./a/@href‘)[0]
        name = div.xpath(‘./a/img/@alt‘)[0]

        detail_page = requests.get(url=detail_url,headers=headers).text
        tree = etree.HTML(detail_page)
        download_list = tree.xpath(‘//div[@class="clearfix mt20 downlist"]/ul/li/a/@href‘)
        download_url = random.choice(download_list)
        data = requests.get(url=download_url,headers=headers).content
        file_name = name + ‘.rar‘
        with open(file_name,‘wb‘) as fp:
            fp.write(data)
            print(file_name,‘下载成功‘)
 
机械电子工程师简历模板.rar 下载成功
设计师英文简历模板下载.rar 下载成功
化妆师个人简历范文.rar 下载成功
 
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
E:\Anaconda3\lib\socket.py in readinto(self, b)
    588             try:
--> 589                 return self._sock.recv_into(b)
    590             except timeout:

OSError: [WinError 10051] 向一个无法连接的网络尝试了一个套接字操作。

During handling of the above exception, another exception occurred:

KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-12-e1e8f04f99ec> in <module>
     26         download_list = tree.xpath(‘//div[@class="clearfix mt20 downlist"]/ul/li/a/@href‘)
     27         download_url = random.choice(download_list)
---> 28         data = requests.get(url=download_url,headers=headers).content
     29         file_name = name + ‘.rar‘
     30         with open(file_name,‘wb‘) as fp:

E:\Anaconda3\lib\site-packages\requests\api.py in get(url, params, **kwargs)
     73 
     74     kwargs.setdefault(‘allow_redirects‘, True)
---> 75     return request(‘get‘, url, params=params, **kwargs)
     76 
     77 

E:\Anaconda3\lib\site-packages\requests\api.py in request(method, url, **kwargs)
     58     # cases, and look like a memory leak in others.
     59     with sessions.Session() as session:
---> 60         return session.request(method=method, url=url, **kwargs)
     61 
     62 

E:\Anaconda3\lib\site-packages\requests\sessions.py in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    531         
    532         send_kwargs.update(settings)
--> 533         resp = self.send(prep, **send_kwargs)
    534 
    535         return resp

E:\Anaconda3\lib\site-packages\requests\sessions.py in send(self, request, **kwargs)
    684 
    685         if not stream:
--> 686             r.content
    687 
    688         return r

E:\Anaconda3\lib\site-packages\requests\models.py in content(self)
    826                 self._content = None
    827             else:
--> 828                 self._content = b‘‘.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b‘‘
    829 
    830         self._content_consumed = True

E:\Anaconda3\lib\site-packages\requests\models.py in generate()
    748             if hasattr(self.raw, ‘stream‘):
    749                 try:
--> 750                     for chunk in self.raw.stream(chunk_size, decode_content=True):
    751                         yield chunk
    752                 except ProtocolError as e:

E:\Anaconda3\lib\site-packages\urllib3\response.py in stream(self, amt, decode_content)
    492         else:
    493             while not is_fp_closed(self._fp):
--> 494                 data = self.read(amt=amt, decode_content=decode_content)
    495 
    496                 if data:

E:\Anaconda3\lib\site-packages\urllib3\response.py in read(self, amt, decode_content, cache_content)
    440             else:
    441                 cache_content = False
--> 442                 data = self._fp.read(amt)
    443                 if amt != 0 and not data:  # Platform-specific: Buggy versions of Python.
    444                     # Close the connection when no data is returned

E:\Anaconda3\lib\http\client.py in read(self, amt)
    445             # Amount is given, implement using readinto
    446             b = bytearray(amt)
--> 447             n = self.readinto(b)
    448             return memoryview(b)[:n].tobytes()
    449         else:

E:\Anaconda3\lib\http\client.py in readinto(self, b)
    489         # connection, and the user is reading more bytes than will be provided
    490         # (for example, reading in 1k chunks)
--> 491         n = self.fp.readinto(b)
    492         if not n and b:
    493             # Ideally, we would raise IncompleteRead if the content-length

E:\Anaconda3\lib\socket.py in readinto(self, b)
    587         while True:
    588             try:
--> 589                 return self._sock.recv_into(b)
    590             except timeout:
    591                 self._timeout_occurred = True

KeyboardInterrupt: 
 

【重点】

  • 问题:往往在进行大量请求发送的时候,经常会报出这样一个错误:HTTPConnectionPool(host:XX)Max retries exceeded with url.
  • 原因:
    • 1.每次数据传输前客户端要和服务器简历TCP连接,为节省传输消耗,默认为keep-alive,即连接一次,传输多次,然而如果连接迟迟不断开的话,连接池满后则无法产生新的连接对象,导致请求无法发送。
    • 2.ip被封
    • 3.请求频率太频繁
  • 解决:如果下列解决未生效,则可以尝试再次执行程序(因为第一次运行Connection可能不生效)
    • 1.设置请求头中的Connection的值为close,表示每次请求成功后断开连接
    • 2.更换请求ip(使用非常简单,之间直接在get/post请求中加个参数)
    • 3.每次请求之间使用sleep进行等待间隔【不推荐,影响效率】
 

解析所有城市名称 https://www.aqistudy.cn/historydata/

In [16]:
import requests
from lxml import etree
import random

headers = 
    ‘Connection‘:‘close‘,# 当请求成功后,马上断开该次请求(及时释放请求池中的资源))
    ‘User-Agent‘:‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER‘

url = ‘https://www.aqistudy.cn/historydata/‘
page_text = requests.get(url=url,headers=headers).text

tree = etree.HTML(page_text)
li_list = tree.xpath(‘//div[@class="bottom"]/ul/li | //div[@class="bottom"]/ul/div[2]/li‘)
for li in li_list:
    city_name = li.xpath(‘./a/text()‘)[0]
    print(city_name)
 
北京
上海
广州
深圳
杭州
天津
成都
南京
西安
武汉
阿坝州
安康
阿克苏地区
阿里地区
阿拉善盟
阿勒泰地区
安庆
安顺
鞍山
克孜勒苏州
安阳
蚌埠
白城
保定
北海
宝鸡
北京
毕节
博州
白山
百色
保山
白沙
包头
保亭
本溪
巴彦淖尔
白银
巴中
滨州
亳州
长春
昌都
常德
成都
承德
赤峰
昌吉州
五家渠
昌江
澄迈
重庆
长沙
常熟
楚雄州
朝阳
沧州
长治
常州
潮州
郴州
池州
崇左
滁州
定安
丹东
东方
东莞
德宏州
大理州
大连
大庆
大同
定西
大兴安岭地区
德阳
东营
黔南州
达州
德州
儋州
鄂尔多斯
恩施州
鄂州
防城港
佛山
抚顺
阜新
阜阳
富阳
抚州
福州
广安
贵港
桂林
果洛州
甘南州
固原
广元
贵阳
甘孜州
赣州
广州
淮安
海北州
鹤壁
淮北
河池
海东地区
邯郸
哈尔滨
合肥
鹤岗
黄冈
黑河
红河州
怀化
呼和浩特
海口
呼伦贝尔
葫芦岛
哈密地区
海门
海南州
淮南
黄南州
衡水
黄山
黄石
和田地区
海西州
河源
衡阳
汉中
杭州
菏泽
贺州
湖州
惠州
吉安
金昌
晋城
景德镇
金华
西双版纳州
九江
吉林
即墨
江门
荆门
佳木斯
济南
济宁
胶南
酒泉
句容
湘西州
金坛
鸡西
嘉兴
江阴
揭阳
济源
嘉峪关
胶州
焦作
锦州
晋中
荆州
库尔勒
开封
黔东南州
克拉玛依
昆明
喀什地区
昆山
临安
六安
来宾
聊城
临沧
娄底
乐东
廊坊
临汾
临高
漯河
丽江
吕梁
陇南
六盘水
拉萨
乐山
丽水
凉山州
陵水
莱芜
莱西
临夏州
溧阳
辽阳
辽源
临沂
龙岩
洛阳
连云港
莱州
兰州
林芝
柳州
泸州
马鞍山
牡丹江
茂名
眉山
绵阳
梅州
宁波
南昌
南充
宁德
内江
南京
怒江州
南宁
南平
那曲地区
南通
南阳
平度
平顶山
普洱
盘锦
蓬莱
平凉
莆田
萍乡
濮阳
攀枝花
青岛
琼海
秦皇岛
曲靖
齐齐哈尔
七台河
黔西南州
清远
庆阳
钦州
衢州
泉州
琼中
荣成
日喀则
乳山
日照
韶关
寿光
上海
绥化
石河子
石家庄
商洛
三明
三门峡
山南
遂宁
四平
商丘
宿迁
上饶
汕头
汕尾
绍兴
三亚
邵阳
沈阳
十堰
松原
双鸭山
深圳
朔州
宿州
随州
苏州
石嘴山
泰安
塔城地区
太仓
铜川
屯昌
通化
天津
铁岭
通辽
铜陵
吐鲁番地区
铜仁地区
唐山
天水
太原
台州
泰州
文昌
文登
潍坊
瓦房店
威海
乌海
芜湖
武汉
吴江
乌兰察布
乌鲁木齐
渭南
万宁
文山州
武威
无锡
温州
吴忠
梧州
五指山
西安
兴安盟
许昌
宣城
襄阳
孝感
迪庆州
锡林郭勒盟
厦门
西宁
咸宁
湘潭
邢台
新乡
咸阳
新余
信阳
忻州
徐州
雅安
延安
延边州
宜宾
盐城
宜昌
宜春
银川
运城
伊春
云浮
阳江
营口
榆林
玉林
伊犁哈萨克州
阳泉
玉树州
烟台
鹰潭
义乌
宜兴
玉溪
益阳
岳阳
扬州
永州
淄博
自贡
珠海
湛江
镇江
诸暨
张家港
张家界
张家口
周口
驻马店
章丘
肇庆
中山
舟山
昭通
中卫
张掖
招远
资阳
遵义
枣庄
漳州
郑州
株洲
 

反爬机制:图片懒加载

  • src属性下有两种图片地址:src/src2,当图片出现在可视化窗口中时出现src,为图片真正地址;当图片不在可视化范围中时图片url为src2;而src2为错误地址,所以当使用爬虫时,出现的是错误的src2地址,所以爬取不到图片。
 

设置请求的代理IP

 
  • 使用方法:
    • 直接在get/post请求中加入proxies=‘类型‘:‘ip‘
    • 代理ip的类型必须和请求url的协议头保持一致
  • 提供代理ip的网站:
    • www.goubanjia.com
    • 快代理
    • 西祠代理
  • 每种代理ip分两种类型:http/https
  • 代理池 [dic1,dic2,dic3...] proxies=
In [24]:
import requests

url = ‘https://www.baidu.com/s?wd=ip‘

page_text = requests.get(url=url,headers=headers,proxies=‘https‘:‘36.111.140.6:8080‘).text

with open(‘./ip.html‘,‘w‘,encoding=‘utf-8‘) as fp:
    fp.write(page_text)

以上是关于python学习——xpath的主要内容,如果未能解决你的问题,请参考以下文章

跨行业如何学习好python?Python学习!

AI测试学习Python学习Python基础学习

AI测试学习Python学习Python基础学习

怎么学习Python,学习Python需要那些学习条件?

0基础该不该学习Python?适合学习吗?

Python学习教程(Python学习路线+Python学习视频):Python数据结构