Python学习笔记之开发简单爬虫和实例

前言:面向对象思想开发一个简单爬虫(Python2.7)


爬虫简单架构:

  1. 爬虫调度器:启动爬虫,停止爬虫,监视爬虫的运行情况
  2. URL 管理器:对将要爬取的 URL 和已经爬取的 URL 的管理
  3. 网页下载器:将指定的网页以 HTML 的形式下载并存储成字符串
  4. 网页分析器:一方面从网页解析出也价值的数据, 另一方面解析出新的 URL 补充进 URL 管理器

URL 管理器作用:

  • 防止重复抓取和循环抓取

URL 管理器功能:

  1. 添加新 URL 到待爬取集合
  2. 判断 URL 是否在容器中(容器包含:待爬取,已爬取)
  3. 判断是否还有待爬取 URL
  4. 获取待爬取 URL
  5. 将 URL 从待爬取移动到已爬取

URL 管理器实现方式:

  1. 内存:待爬取URL集合,已爬取URL集合,采用 set() 数据结构:可避免重复
  2. 关系数据库:urls(url, is_crawled) urls表,url 待爬取,is_crawled 已爬取字段
  3. 缓存数据库:待爬取 URL 集合,已爬取 URL 集合,采用 set() 数据结构:可避免重复, 大公司因为高性能把 URL 存储在缓存数据库

网页下载器(爬虫核心组件):

  1. urllib2 官方模块
  2. requests 第三方强大插件

网页下载器下载网页三种方法:

1. 直接请求: 
    response = urllib2.urlopen(url)
    code = response.getcode()    # 获取状态码,200 表示获取成功
    cont = response.read()    # 读取内容

2. 添加data,http header
    request = urllib2.Request(url)    # 创建 Request 对象
    request.add_data('user', '401575264')    # 添加数据
    request.add_header('User-Agent', 'Mozilla/5.0')    # 添加 http 的 header

3. 添加特殊情景的处理器,如 cookie
    import urllib2, cookielib
    cj = cookielib.CookieJar()    # 创建 cookie 容器
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))    # 创建 opener
    urllib2.install_opener(opener)    # 给 urllib2 安装 opener 来增强处理器
    response = urllib2.urlopen(url)    # 用带有 cookie 的 urllib2 访问网页

网页解析器:

  1. 正则表达式
  2. html.parser #python自带模块
  3. Beatiful Soup #第三方插件
  4. lxml #第三方插件

/* 1 为模糊匹配,234 为结构化解析-DOM (Document Object Modle)树 */

Beautiful Soup语法:

1. 导入模块:from bs4 import BeautifulSoup

2. 创建 BeautifulSoup 对象:
    soup = BeautifulSoup(html_doc, 'html.parser', from_encoding='utf-8')
                        # HTML 文档字符串, HTML 解析器, HTML 文档编码

3. 搜索节点 (find_all, find):
    # 查找所有标签为 a 的节点
    node_list = soup.find_all('a')   
    # 查找所有标签为 a ,链接符合 /view/123.html 形式的节点,第二个参数节点属性用正则表达式匹配
    node_list = soup.find_all('a', href='/view/123.html')    
    #查找所有标签为 div, class为abc, 文字为Python的节点
    node_list = soup.find_all('div',class_='abc', string='Python')

4. 访问节点信息
    node.name    # 获取查找到的结点的标签名称
    node['href']     # 获取查找到的 a 节点的 href 属性
    node.get_text()    # 获取查找到的 a 节点的链接文字

实例:

  • 爬虫调度器 spider_main.py
# -*- coding: utf-8 -*-

import url_manager, html_downloader, html_parser, html_outputer

class SpiderMain(object):
    #初始化各个模块
    def __init__(self):    
        #url管理器
        self.urls = url_manager.UrlManager()    
        #网页下载器
        self.downloader = html_downloader.HtmlDownloader()   
        #网页解析器 
        self.parser = html_parser.HtmlParser()    
        #数据输出器
        self.outputer = html_outputer.HtmlOutputer()    

    #爬虫调度程序
    def craw(self, root_url):   
        #记录爬取第i个url 
        count = 1    
        #添加入口url到待爬取url管理器urls
        self.urls.add_new_url(root_url)    
        #当url管理器有待爬取url
        while self.urls.has_new_url():    
            #异常处理:有些链接可能已经无法访问等
            try:    
                #从待爬取urls中获取一个url
                new_url = self.urls.get_new_url()    
                print 'craw %d : %s' %(count, new_url)
                #启动下载器下载网页并保存到html_cont
                html_cont = self.downloader.download(new_url)    
                #启动解析器解析下载好的网页, 并得到新的url列表和新的数据
                new_urls, new_data = self.parser.parse(new_url, html_cont)    
                #将新的url补充到待爬取url管理器urls
                self.urls.add_new_urls(new_urls)    
                #收集数据
                self.outputer.collect_data(new_data)    
                #如果爬取达到100个url就停止爬取
                if count == 100:    
                    break
                #每爬取一个url, 计数加1
                count += 1    
            except:
                print 'craw failed'
        #输出收集好的数据
        self.outputer.output_html()    


if __name__=="__main__":
    #入口 URL
    root_url = "https://baike.baidu.com/item/Python/407313"
    #实例化爬虫对象
    obj_spider = SpiderMain()
    #爬取网页并处理数据
    obj_spider.craw(root_url)
  • URL 管理器 url_manager.py
# -*- coding: utf-8 -*-

class UrlManager(object):
    def __init__(self):
        #待爬取url管理器
        self.new_urls = set()    
        #已爬取url管理器
        self.old_urls = set()    

    #向url管理器中添加一个新的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管理器中添加批量url
    def add_new_urls(self, urls):    
        if urls is None or len(urls) == 0:
            return 0
        for url in urls:
            self.add_new_url(url)

    #判断url管理器中是否有新的带爬取的url
    def has_new_url(self):    
        return len(self.new_urls) != 0

    #从url管理器中获取一个新的待爬取url
    def get_new_url(self):    
        #pop会从列表中删除一个元素, 并返回那个元素
        new_url = self.new_urls.pop()    
        self.old_urls.add(new_url)
        return new_url
  • 网页下载器 htnl_downloader.py
# -*- coding: utf-8 -*-

import urllib2

class HtmlDownloader(object):
    def download(self, url):
        if url is None:
            return None

        self.response = urllib2.urlopen(url)

        #if response.getcode() != 200:
            #return None
        return self.response.read()
  • 网页分析器 html_parser.py
# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup
import re

class HtmlParser(object):   

    def _get_new_urls(self, page_url, soup):
        #正则表达式获取所有符合格式的链接地址
        links = soup.find_all('a',href=re.compile(r'/item/[A-Z0-9%/]+'))
        #链接不完整时要补上
        new_links = set(map(lambda x: 'https://baike.baidu.com'+x['href'], links))
        return new_links

    def _get_new_data(self, page_url, soup):
        res_data = {}
        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
        soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
        #获取新url列表
        new_urls = self._get_new_urls(page_url, soup)    
        #获取新数据
        new_data = self._get_new_data(page_url, soup)    
        return new_urls, new_data
  • 数据输出器 html_outputer.py
# -*- coding: utf-8 -*-

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):
        f = open('output.html', 'w')
        f.write('<html>')
        f.write('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />')
        f.write('<body>')

        for data in self.datas:
            f.write('<table border="1" style="BORDER-COLLAPSE: collapse">')
            f.write('<tr>')
            f.write('<td>%s</td>' %data['url'])
            f.write('<td>%s</td>' %data['title'].encode('utf-8'))
            f.write('<td>%s</td>' %data['summary'].encode('utf-8'))
            f.write('</tr>')
            f.write('</table>')
            f.write('<br/>')

        f.write('</body>')
        f.write('</html>')
        f.close()

猜你喜欢

转载自blog.csdn.net/qq_37509235/article/details/80507747