关于爬虫我原来用的一直是pyquery解析库,最近尝试了一下xpath,发现它真的很强大。
下面是一个xpath的一个小栗子。
这是一个题库的网址,我们要做的就是把所有的题目和答案爬取下来,一共是16页。
先用request获取页面的html看一下。
response = requests.get("http://syszr.hfut.edu.cn/redir.php?catalog_id=6&tikubh=4200&cmd=learning")
print(response.text.encode("latin1").decode("gbk"))
嗯,是可以直接爬下来的,没有任何反爬措施。那我们需要做的部分就只是解析网页了。(ps. 加上encode(“latin1”).decode(“gbk”)是为了防止中文乱码)
接下来是具体的步骤。
import requests
from lxml import etree
lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。
因为我们一共要爬取16页的内容,每一页url的设计肯定是有规律的。根据这个规律构造url,就不需要一页页地手动爬取了。
切换到第二页,url如下。
第三页。
已经发现规律了吧。每个url除了page页数的不同,其他都是一样的。
将page作为一个参数构造url如下。
URL = "http://syszr.hfut.edu.cn/redir.php?catalog_id=6&cmd=learning&tikubh=4200&page={page}"
def scrape_index(page):
url = URL.format(page=page)
response = requests.get(url)
return response
这里定义了一个爬取每一页的函数,将page作为参数传进去。
TOTAL_PAGE = 16
questions = []
answers = []
for page in range(1, TOTAL_PAGE + 1):
response = scrape_index(page)
html = etree.HTML(response.text.encode("latin1").decode("gbk"))
q = html.xpath('//*[@id="shiti-content"]/div/h3')
a = html.xpath('//*[@id="shiti-content"]//span')
for item in q:
questions.append(item.text)
for item in a:
answers.append(item.text.replace("\r\n", ''))
这是代码最主要的部分,其实也就几行而已。我们首先定义了一个总的页数TOTAL_PAGE,然后通过for循环依次爬取所有的页面。questions和answers两个list分别用来保存获得的解析出来的数据。
接下来我们重点讲一下xpath解析的部分。
先回到原来的网页,打开开发者模式,定位到我们想要的元素。
可以看到题目由h3元素表示,答案用span元素表示。通过右键,选择Copy->Copy XPath,可以获得当前元素的xpath表达式。
获得的表达式如下。
//*[@id="shiti-content"]/div[1]/h3
如果我们直接通过这个表达式解析元素,获得的结果就只有一条。因为它匹配的是div元素列表中第一个元素下的h3元素。我们将它改成"//*[@id=“shiti-content”]/div/h3"就可以匹配到div下的所有h3元素。“//”表示从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
右键复制得到当前span元素的xpath表达式如下。
//*[@id="shiti-content"]/span[1]
同理,这里的span[1]表示取列表中的第一个元素。我们把它改成span就可以取到所有元素了。
以下列出了一些常用的xpath路径表达式。
https://www.w3school.com.cn/xpath/xpath_syntax.asp.
最后来看一下结果。
OK,完成。还有个小问题要注意一下,xpath解析出来的内容有时会包含"\r\t"等符号,我们用replace函数将它替换掉就可以了。