• Python爬虫学习笔记(二)


    爬虫接触了也有段时间,跟着网上的一些教程,不仅做出了一些实用的小工具,而且对于使用Python爬虫的整个流程有了大致的了解,也知道了爬虫是怎么回事。以前做的一些小的试验,陆续也都会写成博客,今天记录的, 是我在慕课网上(http://www.imooc.com/learn/563)学到的一个爬虫框架,结构清晰合理,很值得学习,这里实现的只是爬虫最简单的功能,不涉及用户的登陆和Cookie验证,当然,这些都是可以扩充到这个框架里的。

    首先,先讲一讲整个爬虫框架的思路。

    先确定任务:就是以百度百科Python词条为根,陆续爬下相关的1000个网页,这个阈值自然是可以调整的。都知道,在百度百科页面,很多关键词都是有链接的,链接到另一个网页上,可以搜索到更多的链接,就这样循环爬取,直到1000个为止。

    代码大概用了150多行,写成了五个对象,SpiderMain、UrlManger、HtmlDownloader、HtmlParser和HtmlOutputer。SpiderMain是所有其余对象的入口,UrlManger负责管理url,用了两个set(),一新一旧,因为已经使用过的链接就没有必要再爬一次了,就放在old里,从网页中解析出来的新的链接放在new里。HtmlDownloder负责将网站的数据下载到本地,HtmlParser负责把下载到本地的网页数据里的相关数据解析出来,这里解析的,一个是链接,另一个是词条的summary,HtmlOupter就负责最后结果的输出。整个框架对应的是一个线性的流程,结构清晰。

    下面看代码。

    # 实例:爬取python相关一千个网址

    # 先导入有关的库,这一步很重要

    import urllib2

    from bs4 import BeautifulSoup # 很好用的解析器

    import re # 很容易遗漏,即使漏掉程序也不报错,很隐蔽

    # 程序的入口类

    class SpiderMain(object):

    # 初始化对象

    def __init__(self):

    self.urls = UrlManger()

    self.downloader = HtmlDownloader()

    self.parser = HtmlParser()

    self.outputer = HtmlOutputer()

    # 最核心的方法

    def craw(self, root_url):

    count = 1 # 方便计数

    self.urls.add_new_url(root_url)

    while self.urls.has_new_url():

    try:

    # 取出url

    new_url = self.urls.get_new_url()

    print 'craw %d : %s' % (count, new_url)

    # 根据url下载网页内容

    html_cont = self.downloader.download(new_url)

    # 将网页内容解析为需要的数据

    new_urls, new_data = self.parser.parse(new_url, html_cont)

    # 将数据各自存放

    self.urls.add_new_urls(new_urls)

    self.outputer.collect_data(new_data)

    if count == 10:

    break

    count += 1

    except:

    print 'craw failed'

    # 将数据输出为html文件

    self.outputer.output_html()

    class UrlManger(object):

    # 初始化,两个set(),因为set()里的数据不会重复

    def __init__(self):

    self.new_urls = set()

    self.old_urls = set()

    # 添加一个url

    def add_new_url(self, url):

    if url is None:

    return

    if url not in self.new_urls and url not in self.old_urls:

    self.new_urls.add(url)

    # 添加多个url,循环使用add_new_url,很巧妙

    def add_new_urls(self, urls):

    if urls is None or len(urls) == 0:

    return

    for url in urls:

    self.add_new_url(url)

    # 判断是否还有没有爬取的url,返回bool值

    def has_new_url(self):

    return len(self.new_urls) != 0

    # 获取一个url

    def get_new_url(self):

    new_url = self.new_urls.pop() # pop用的极好,不仅取得了数据,而且将元素从set()中删除

    self.old_urls.add(new_url) # 使用过的url要放在old_urls里,避免重复爬取

    return new_url

    # 下载器

    class HtmlDownloader(object):

    def download(self, url):

    if url is None:

    return None

    # Python自带的urllib2下载模块

    response = urllib2.urlopen(url)

    # 可以用getcode()方法判断是否下载成功

    if response.getcode() != 200:

    return None

    return response.read()

    # Html解析器,核心,倒着看

    class HtmlParser(object):

    def get_new_urls(self, soup):

    # 解析出来的url也放在一个set()里

    new_urls = set()

    # 正则表达式是根据网页源代码分析出来的

    links = soup.find_all('a', href=re.compile(r'/view/d+.htm'))

    for link in links:

    new_url = link['href']

    # 解析出来的链接是个相对url,要合成成一个绝对url

    page_url = 'http://baike.baidu.com'

    new_full_url = page_url + new_url

    new_urls.add(new_full_url)

    return new_urls

    def get_new_data(self, page_url, soup):

    # 先构造一个字典

    res_data = {}

    # url

    res_data['url'] = page_url

    # <dd class="lemmaWgt-lemmaTitle-title"><h1>Python</h1>

    title_node = soup.find('dd', class_='lemmaWgt-lemmaTitle-title').find('h1')

    res_data['title'] = title_node.get_text()

    # <div class="lemma-summary" label-module="lemmaSummary">

    summary_node = soup.find('div', class_='lemma-summary')

    res_data['summary'] = summary_node.get_text()

    return res_data

    def parse(self, page_url, html_cont):

    if page_url is None or html_cont is None:

    return

    # BeautifulSoup固定用法,先构造一个BeautifulSoup对象

    soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')

    # 方法里面嵌套方法,值得学习

    new_urls = self.get_new_urls(soup)

    new_data = self.get_new_data(page_url, soup)

    return new_urls, new_data

    # 输出器

    class HtmlOutputer(object):

    def __init__(self):

    self.datas = []

    def collect_data(self, data):

    if data is None:

    return

    self.datas.append(data)

    # 输出为html文件

    def output_html(self):

    # 以写的形式打开文件

    fout = open('D:/Learn/Code/python/pachong/output.html', 'w')

    # 这一句必须加上,否则中文字符会显示乱码

    fout.write('<meta charset=utf-8" />')

    fout.write('<html>')

    fout.write('<body>')

    fout.write('<table>')

    fout.write('<th>URL</th>')

    fout.write('<th>Title</th>')

    fout.write('<th>Summary</th>')

    for data in self.datas:

    fout.write('<tr>')

    fout.write('<td>%s</td>' % data['url'])

    # 注意中文字符的编码

    fout.write('<td>%s</td>' % data['title'].encode('utf-8'))

    fout.write('<td>%s</td>' % data['summary'].encode('utf-8'))

    fout.write('</tr>')

    fout.write('</table>')

    fout.write('</body>')

    fout.write('</html>')

    # 不要忘记关闭文件

    fout.close()

    # 初始化程序

    root_url = 'http://baike.baidu.com/view/21087.htm'

    obj_spider = SpiderMain()

    obj_spider.craw(root_url)

    结果如下:

    打开output.html

    没什么问题!

    总结:虽然名为一个简易的爬虫框架,其实已经不简单,难得的是思路如此清晰,结构很严谨,很多细节都很值得玩味。就凭这一个项目,也能看得出自己与高手之间的差距。从这些代码中,我领悟到,在写代码之前,一定要先理清思路;其次,这里用到了面向对象(oop)的方式,功能各自独立,又互相补充,这不就是高内聚、低耦合吗?要学的还有很多!

    源代码地址:https://github.com/Lucifer25/Learn-Python/blob/master/Web-Crawler/SimpleFramework.py

    参考资料:http://www.imooc.com/learn/563

    却道,此心安处是吾乡
  • 相关阅读:
    oracle内存粒度
    知乎--软件架构设计 性能系列
    VAMEI 图解的博客
    泰晓科技
    高性能高并发系统的稳定性保障
    如何提高Linux下块设备IO的整体性能?
    Unix操作系统LD_PRELOAD简介
    浅析keepalived vip漂移原理与VRRP协议
    SQL:将查询结果插入到另一个表的三种情况
    C# Graphics中有关绘图质量的几个Mode
  • 原文地址:https://www.cnblogs.com/lucifer25/p/5835615.html
Copyright © 2020-2023  润新知