欢迎关注公众号K的笔记阅读博主更多优质学习内容
上一篇内容:Python爬虫初级(七)—— 丁香园评论留言板爬取
前一篇文章的末尾我们提到,可以使用 lxml + xpath 提取文章内容,在这篇文章中,我们将对 lxml 与 xpath 进行详细阐述,在这之前我们使用 pip install lxml 安装 lxml 库。
Xpath 语法
XPath 即为 XML 路径语言(XML Path Language),它是一种用来确定 XML 文档中某部分位置的语言。而 XML 我们在这篇文章中已经提到了,它的语法与 HTML 基本一致,可以说是通过 HTML发展而来的通用表达形式。
路径表达式
XPath 使用路径表达式在 XML 文档中选取节点,节点是通过沿着路径选取的。下面列出了最常用的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有节点 |
/ | 从根节点选取 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
Predicates
表达式 | 描述 | 实例 | 解释 |
---|---|---|---|
[n] | 选择某节点的第n(n>=1)个子节点 | xpath(“//h//a[1]”) | 选择h节点下第1个a节点 |
[last()] | 选择某节点的最后一个子节点 | xpath(“//h//a[last()]”) | 选择h节点下最后一个a节点 |
[@attribute] | 选择节点带有attribute属性的节点 | xpath(“//img[@src]”) | 选择带有src属性的img节点 |
[@attribute=value] | 选择带有attribute属性值的节点 | xpath(“//a[@href=”aaa.jpg”]”) | 选择href属性值为aaa.jpg的a节点 |
* | 任意匹配元素或者属性 | xpath(“//a/“),xpath(“//a[@]”) | 选择a节点下的所有子节点,选择带有属性的a节点 |
模糊搜索与匹配
函数 | 用法 | 解释 |
---|---|---|
starts-with | xpath(“//div[starts-with(@id,’user’)]”) | 选择id值以user开头的div节点 |
contains | xpath(“//div[contains(@id,’user’)]”) | 选择id值包含user的div节点 |
and | xpath(“//div[starts-with(@class,”login”) and contains(@id,’user’)]”) | 选择class值以login开头和id值包括user的div节点 |
text() | xpath(“//div[starts-with(text(),”mytest”)]”) | 选取节点文本包含myest的div节点 |
xpath轴
轴名称 | 表达式 | 描述 |
---|---|---|
ancestor | xpath(“./ancestor:: *”) | 选取当前节点的所有父辈节点 |
ancestor-or.self | xpath(“./ancestor-or-self:: *”) | 选取当前节点的父辈节点和节点自身 |
child | xpath(“./child:: *”) | 选择当前节点的所有子节点 |
descendant | xpath(“./descendant:: *”) | 选择当前节点的所有后代节点(子节点、孙节点等) |
follow | xpath(“./following:: *”) | 选取当前节点结束标签后的所有节点 |
follow-sibling | xpath(“./follow-sibling:: *”) | 选取当前节点之后的兄弟节点 |
preceding | xpath(“./preceding:: *”) | 选取当前开始标签前的所有节点 |
lxml 使用
首先需要导入库:from lxml import etree,下面这行代码 lxml 会将 html 文本转成 xml 对象
tree = etree.HTML(html)
我们要使用 lxml + Xpath 从源代码直接获取信息时,会使用 tree.xpath() 语句,括号内的是所需获取内容的一些形式,具体形式在上面已经列出了,比如我们要获取丁香园页面的用户名称信息,首先查看该页面源代码:
我们可以使用:
tree.xpath('//div[@class=“auth”]/a/text()')
我们想要获取评论信息:
我们可以使用
tree.xpath('//td[@class=“postbody”]')
我们需要注意的是,当我们将文本对象转换为了 lxml 对象后,实际获取内容的方式也发生了改变,参看下面代码:
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
# 返回 <Element html at 0x233468e5788>
result = etree.tostring(html)
print(result)
# 返回 text 中信息
代理 IP 爬取
介绍完 xpath 的基本内容,我们下面来实战一个非常有用的爬虫 —— 爬取代理IP,其具体页面内容如下:
我们点击查看源代码,可以发现相关的IP地址直接出现在页面中,因此我们能够通过现有知识进行爬取:
下面是测试代码:
>>> from lxml import etree
>>> import requests
>>> url = "https://www.xicidaili.com/"
>>> headers = {"user-agent":"Mozilla/5.0"}
>>> res = requests.get(url, headers=headers)
>>> res.status_code
200
>>> res.encoding = res.apparent_encoding
>>> text = res.text
>>> html = etree.HTML(text)
>>> html.xpath('/html/body/div[1]/div[2]/div[1]/div[1]/table/tbody/tr[3]/td[2]')
[]
>>> ip_ = html.xpath('/html/body/div[1]/div[2]/div[1]/div[1]/table//tr[3]/td[2]')
[<Element td at 0x233473b8d08>]
>>> result = etree.tostring(ip_[0])
>>> result
b'<td>119.4.13.26</td>\n
事实上,我们在实际使用时,想要获得 Xpath 不一定会直接一行行代码看过去,更可能直接复制 Xpath:
我们右键移至复制,会出现一个名为“复制Xpath”的选项,点击复制Xpath 后即可得到相应的 Xpath,但在这里有一点需要注意的是,Xpath 会给页面 HTML 树进行自动补齐,也就是说会出现一些原来没有的东西,就会出现上面代码中倒数第六行出现空返回的问题,这时候就需要我们去仔细检查哪里的信息可能是多余的。
下面我们展示完整代码:
import pickle#我们封装数据要用到的库
import requests
from lxml import etree
#请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36',
}
def construct_url():#老样子,先构建url列表,我这就爬一页
urls = []
for i in range (1,2):
url = 'https://www.xicidaili.com/nn/'+str(i)
urls.append(url)
return urls
def get_proxy(url):#从数据中解析出ip数据,并验证ip是否可用
res = requests.get(url,headers=headers)
html = etree.HTML(res.text,etree.HTMLParser())
#把返回的数据解析成etree._Element对象,这样才可以使用Xpath语句提取数据
ip = html.xpath('//*[@id="ip_list"]/tr/td[2]/text()')#ip
ip_port = html.xpath('//*[@id="ip_list"]/tr/td[3]/text()')#端口号
ip_type = html.xpath('//*[@id="ip_list"]/tr/td[6]/text()')#网络类型
lis = []
for i,j,k in zip(ip,ip_port,ip_type):
try:
proxies = {
k.lower():i+':'+j#注意到爬下来的是HTTP或者是HTTPS,我们要将它们变成http或https
}
print(proxies)#把IP输出来看看
requests.get('https://www.baidu.com/',headers=headers,timeout=1,proxies=proxies)
#用这个IP发一个请求给百度,测试一下这个IP能不能用
lis.append(proxies)#如果没有超时,说明能用,存进列表里
print('http://'+i+':'+j+" is fine")
except:
print('http://'+i+':'+j+" is timeout")#超时抛出错误,输出哪个IP超时
return lis#返回可用IP列表
if __name__=='__main__':
urls = construct_url()
lis = []
for url in urls:
rlist = get_proxy(url)
lis += rlist
pickle.dump(lis,open('ip.pkl','wb'))#将可用的IP进行存储
print('----可用IP---')
for item in lis:
print(item)
print('--- END ---')
# 代码直接来源:https://blog.csdn.net/weixin_41169182/article/details/88676729
下一篇内容:Python爬虫初级(九)—— ajax 详解