WSWP(用python写爬虫)笔记二:实现链接获取和数据存储爬虫
来源:互联网 发布:与美食有关的句子 知乎 编辑:程序博客网 时间:2024/05/16 17:27
前面已经构建了一个获取链接的爬虫模块,现在继续完善这个爬虫。
分析网页
要对一个网页中的数据进行提取,需要先对网页的结构有一个大体的了解,通过在浏览器查看网页源代码的方法就能实现。
在了解到网页的结构后,接下来便是获取自己想要的数据了,个人比较喜欢用Chrome浏览器的检查元素的方式来定位数据在html源码中的位置(根据个人喜好来选择,不过建议直接使用chrome自带的,后面获取解析内容的css selector或XPath比较方便)。例如,我想要爬取某篇blog的内容,直接在Chrome中打开blog的url,并在感兴趣的内容上依次点击右键 -> 检查便能定位到数据在网页源码中的位置。如图所示:
结果如下:
如此便得到了想要爬取的内容存在于标签< div class=”markdown_views”>之中。
接下来便是通过解析网页获取到目标内容并进行抓取了。
数据抓取
测试url: http://example.webscraping.com/places/default/view/China-47
抓取的数据: 国土面积
数据在网页源码中的位置:
可以看出所想抓取的数据的位置在< td class=”w2p_fw”>中。
接下来介绍三种抓取数据的方式,首先是经典的正则表达式匹配法,然后是python中比较流行的Beautifulsoup模块,最后是强大的lxml模块。
正则表达式
对于正则表达式不清楚的,可以先阅读 正则表达式。
通过正则表达式抓取面积数据时,首先尝试匹配< td class=”w2p_fw”>中的内容,示例如下:
# re_test.pyfrom wswp_1_pro.downloader import downloadimport reurl = 'http://example.webscraping.com/places/default/view/China-47'html = download(url, headers={'User-agent':'wswp'}, proxy=None, numRetries=2)for content in re.findall('<td class="w2p_fw">(.*?)</td>', str(html)): print(content)
结果如下:
从上面的结果中可以看出,解析出了很多内容,返回到源码中可以看出多个属性都使用了< td class=”w2p_fw”>标签。如果想要分离出面积属性,可以只选择其中的第二个元素即可。
# re_test.pyfrom wswp_1_pro.downloader import downloadimport reurl = 'http://example.webscraping.com/places/default/view/China-47'html = download(url, headers={'User-agent':'wswp'}, proxy=None, numRetries=2)# for content in re.findall('<td class="w2p_fw">(.*?)</td>', str(html)):# print(content)area = re.findall('<td class="w2p_fw">(.*?)</td>', str(html))[1]print("面积为:", area)
结果如下:
使用正则表达式的缺点是不够灵活,如果网页的将属性值更改以后,则需要重新更改正则表达式。
BeautifulSoup模块
BeautifulSoup在网页数据解析时非常强大,提供定位内容的便捷接口。
首先通过pip安全该模块。
pip install bs4
使用BeautifulSoup的第一步是将已经下载好的html内容解析为soup文档(对网页内容进行正确解析并对未闭合的标签进行闭合)。然后可以使用find()和findall()来定位所需要的元素了。
以下是使用BeautifuSoup提取示例国家面积数据的完整代码:
from bs4 import BeautifulSoupfrom wswp_1_pro.downloader import downloadurl = 'http://example.webscraping.com/places/default/view/China-47'html = download(url, headers={'User-agent':'wswp'}, proxy=None, numRetries=2)soup = BeautifulSoup(html, "html.parser")# 先定位面积的父标签tr = soup.find(attrs={'id':'places_area__row'})# 通过父标签再来定位子标签td = tr.find(attrs={'class':'w2p_fw'})area = td.textprint("面积为:", area)
lxml模块
lxml官网点击这里^_^。
lxml pdf文档下载。
lxml通过pip安装。
pip install lxml
lxml模块跟BeautifulSoup一样,将下载的html解析为统一格式(修复不闭合的标签)。示例代码如下:
from wswp_1_pro.downloader import downloadimport lxml.htmlurl = 'http://example.webscraping.com/places/default/view/China-47'html = download(url, headers={'User-agent':'wswp'}, proxy=None, numRetries=2)tree = lxml.html.fromstring(str(html))# 从面积的父标签开始提取内容td = tree.cssselect('tr#places_area__row > td.w2p_fw')[0]area = td.text_content()print("面积为:", area)
上述代码中使用cssselect(CSS选择器)可通过pip直接安装。
lxml有几种不同的方法进行元素的选择,比如XPath选择器、CSS选择器和类似于BeautifulSoup中的find方法。选用CSS选择器,因为其更加简洁,并且在能够解析动态内容。(可通过Chrome中选择检查元素,copy –> Copy selector来直接从浏览器中获取到CSS选择器的参数,然后再根据实际情况进行精简处理),CSS选择器的参考教程请点这里。
使用XPath选择器的代码如下:
from wswp_1_pro.downloader import downloadimport lxml.htmlurl = 'http://example.webscraping.com/places/default/view/China-47'html = download(url, headers={'User-agent':'wswp'}, proxy=None, numRetries=2)tree = lxml.html.fromstring(str(html))# 从面积的父标签开始提取内容td = tree.xpath('//tr[@id="places_area__row"]/td[@class="w2p_fw"]')[0]area = td.text_content()print("面积为:", area)
以上就是从网页中获取感兴趣的数据的方法,接下来就是向爬虫程序中添加针对爬取到的数据的处理方法了。
为链接爬虫添加抓取回调
前面实现的链接爬虫如果想要复用到抓取其他网站的数据,需要添加一个callback参数参与抓取行为。callback是一个函数,在发生某个特定事件之后会调用该函数(在本例中会在网页下载完成后调用)。
实现的callback函数包含url和html两个参数,并且可以反回一个待爬取的url列表。在linkCrawler.py中添加代码,如下所示:
# linkCrawler.pydef linkCrawler(..., scrapeCallBack=None): ... links = [] if scrapeCallBack: links.extend(scrapeCallBack(url, html) or []) ...
现在只需要对传入的callback函数进行定制化处理,就能使用该爬虫对其他网站进行爬取了。下面对使用lxml抓取国土面积的代码进行修改,使其能在callback中使用。
import reimport lxml.htmlfrom wswp_1_pro.linkCrawler import linkCrawlerFIELDS = ['area', 'population', 'iso', 'capital', 'continent', 'tld', 'currency_code', 'currency_name', 'phone', 'postal_code_format', 'postal_code_regex', 'languages', 'neighbours']def scrapeCallBack(url, html): if re.search('/view/', url): tree = lxml.html.fromstring(str(html)) row = [tree.cssselect('table > tr#places_%s__row > td.w2p_fw' % field)[0].text_content() for field in FIELDS] for text, content in zip(FIELDS,row): print(text + ' : ' + content)if __name__ == "__main__": linkCrawler('http://example.webscraping.com/', '.*?/(index|view)', maxDepth=2, scrapeCallBack=scrapeCallBack)
结果如下:
上一个callback函数会去抓取国家数据,然后显示出来。通常情况下,在抓取网站时,更希望能够复用这些数据,下面对callback的功能进行扩展,将得到的结果数据保存到CSV表格中,代码如下:
# callBack_test.pyimport csvimport reimport lxml.htmlfrom wswp_1_pro.linkCrawler import linkCrawlerclass ScrapeCallback: def __init__(self): self.writer = csv.writer(open('countries.csv', 'w')) self.fields = ('area', 'population', 'iso', 'country', 'capital', 'continent', 'tld', 'currency_code', 'currency_name', 'phone', 'postal_code_format', 'postal_code_regex', 'languages', 'neighbours') self.writer.writerow(self.fields) def __call__(self, url, html): if re.search('/view/', url): tree = lxml.html.fromstring(html) row = [] for field in self.fields: row.append(tree.cssselect('table > tr#places_{}__row > td.w2p_fw'.format(field))[0].text_content()) self.writer.writerow(row)if __name__ == '__main__': linkCrawler('http://example.webscraping.com/', '/(index|view)', maxDepth=3, scrapeCallBack=ScrapeCallback())
结果如下:
上面的callback,使用了回调类,而不再是回调函数,以便保持csv中writer的属性状态。csv的writer属性在构造方法中进行了实例化处理,然后在 __call__ 方法中执行多次写操作。__call__()是一个特殊的方法,在对象作为函数被调用是会调用该方法。
成功了,完成了一个可以工作的数据获取爬虫了。
- WSWP(用python写爬虫)笔记二:实现链接获取和数据存储爬虫
- WSWP(用 python写爬虫) 笔记五:并发爬虫
- WSWP(用python写网络爬虫)笔记 一:实现简单爬虫
- WSWP(用 python写爬虫) 笔记四:实现缓存功能
- WSWP(用 python写爬虫) 笔记三:为爬虫添加缓存网页内容功能
- 用Python写网络爬虫系列(二)------数据获取
- python 爬虫笔记(二)
- python爬虫-->获取数据
- 写网络爬虫学python(二)
- 学习python写网络爬虫(二)
- 用python 写网络爬虫 学习笔记
- 《用python写网络爬虫》笔记1
- 《用python写网络爬虫》笔记2
- 用python写网络爬虫笔记
- 《用python写网络爬虫》笔记3
- python 爬虫获取网站信息(二)
- 用python写爬虫
- 跟踪链接实现python爬虫
- Kotlin取时间
- docker 容器操作
- AndroidStudio升级问题
- Ubuntu16.04中文输入法安装初战
- PHP使用curl提交数据的三种方法
- WSWP(用python写爬虫)笔记二:实现链接获取和数据存储爬虫
- 高德地图
- 二叉树非递归遍历 (前,中,后) c
- 互联网分层架构,为啥要前后端分离?
- JavaEE结合quartz实现订单自动失效功能
- java学习第37天,集合转换数组
- 2017.10.25 LeetCode
- UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK编码
- OkHttp简单封装,添加泛型