Python爬虫初级(八)—— lxml 详解及代理IP爬取

前一篇文章的末尾我们提到,可以使用 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 对象

1
tree = etree.HTML(html)

我们要使用 lxml + Xpath 从源代码直接获取信息时,会使用 tree.xpath() 语句,括号内的是所需获取内容的一些形式,具体形式在上面已经列出了,比如我们要获取丁香园页面的用户名称信息,首先查看该页面源代码:
用户信息我们可以使用:

1
tree.xpath('//div[@class=“auth”]/a/text()')

我们想要获取评论信息:
评论信息
我们可以使用
1
tree.xpath('//td[@class=“postbody”]')

我们需要注意的是,当我们将文本对象转换为了 lxml 对象后,实际获取内容的方式也发生了改变,参看下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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主页
我们点击查看源代码,可以发现相关的IP地址直接出现在页面中,因此我们能够通过现有知识进行爬取:

西刺代理IP主页源代码
下面是测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> 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,但在这里有一点需要注意的是,Xpath 会给页面 HTML 树进行自动补齐,也就是说会出现一些原来没有的东西,就会出现上面代码中倒数第六行出现空返回的问题,这时候就需要我们去仔细检查哪里的信息可能是多余的。

下面我们展示完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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 ---')
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2020-2021 chenk
  • 由 帅气的CK本尊 强力驱动
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信