其实这篇不涉及什么高大上的反爬,但是实在不知道要把这篇文章归类到哪里,就直接先扔这里吧。
先吐槽一句:萌娘百科绝对是我再也不想爬第二次的网站。
第二句:(真理)把网站弄得越乱越让人摸不着头脑,就是最好的反爬手段,比什么加密都管用。
先看需求:爬取萌娘百科中所有游戏角色介绍
正常对于一个百科类网站而言,一拿到这个需求,第一反应肯定是先弄一个游戏角色名的list,然后挨个进行search,抓取返回的页面内容。然而当我想进一步索要游戏角色名的list时,得到的回复是:没有现成的游戏角色名,把萌娘上有的游戏角色爬了就行。
歪日???
一开始的思路还是先自己整一个游戏角色名的list,这样我面临一个很大的问题就是,量上不去。即便在度娘的帮助下,我能找到的游戏角色也就那么几个,再去除一些萌娘没有收录的词条,就所剩无几了。
没辙,死磕网站吧。
然后我找到了这个:https://mzh.moegirl.org.cn/Special:特殊页面
我一度以为自己发现了宝藏,直到我在让人眼花撩乱的页面中怎么也找不到我要的游戏角色分类…这分类都是啥啊,太乱了吧。
后来勉强又找到了两个看上去能爬的、勉强贴近我的需求的页面:https://mzh.moegirl.org.cn/Category:分类、https://mzh.moegirl.org.cn/Category:电子游戏角色列表
可算是有点东西了。虽然这肯定不是所有的链接,但好歹能缓解我的燃眉之急。
这也算是给了一个新的思路:爬这种百科类的网站的时候,可以尝试去定位网站的根目录,然后从根目录里沿着网站的分类树一级一级地往下搜索,把需要的链接抓全。
这是爬萌娘遇到的第一个问题,勉强算解决了吧。
爬的过程中发现,萌娘的每一个词条的最底端,会附上一大堆的相关或者参考链接,有很多游戏角色都会给总给在一个类似下边的大表中:
这张大表除了有游戏角色,还有一堆乱七八糟的分类,并且,每个页面下的大表可能都不一样,当然也有可能一样。
那我只要里边的角色名和链接,我该怎么操作?
观察页面源代码发现,表格的第一列(即带有xx角色字样的标题列)跟其右边的内容是一一对应的,同在一个tr标签下,也就是说,他们是兄弟节点。
XPath的
following-sibling
轴可以选择当前节点之后的兄弟节点:
from lxml import etree
# 使用XPath选择包含"角色"文字的td标签,并获取其下一个兄弟节点的内容
selected_elements = tree.xpath('//td[contains(text(), "角色")]/following-sibling::td[1]')
在XPath表达式中,//td[contains(text(), "角色")]/following-sibling::td[1]
表示选择包含"角色"文字的td标签,然后获取其下一个兄弟节点的内容。
最终的代码如下:
def get_table_links(url):
headers = {
'User-Agent': random.choice(USER_AGENTS_LIST),
'Referer': url
}
resp = requests.get(url, headers=headers)
text = resp.text
html = etree.HTML(text)
# 所有罗列了游戏角色的表格定位
hrefs = html.xpath('(//table[@class="navbox"])[1]//td[contains(text(), "角色")]/following-sibling::td[1]//a[not(@class="new") and not(starts-with(@href, "http"))]/@href')
return hrefs
其中,//a[not(@class="new") and not(starts-with(@href, "http"))]
意味获取所有class不为“new”的a标签下的链接,之所以要排除掉这个class,是因为萌娘中,class="new"意味着该页面不存在。
最后感慨,我确实、十分、非常佩服萌娘网站的设计者,顶礼膜拜的那种。