Python開發簡單爬蟲

本文在學習慕課網 瘋狂的螞蟻crazyant 的課程後寫做,文中截圖部分來自於視頻,感謝視頻做者。你們也能夠經過點擊這裏觀看視頻學習,老師講得賊棒!html

What's 爬蟲

通俗的講,爬蟲就是經過一個URL開始,自動獲取數據的「網絡機器人」。node

簡單的爬蟲架構

pic

  1. URL管理器記錄爬取過的URL和未爬取的URL
  2. 從URL管理器中獲取一個未爬取的URL下載網頁內容
  3. 解析爬取到的內容,將價值數據存儲起來,將爬到的新URL傳遞給URL管理器存儲爲未爬取的URL,並從2開始循環執行

URL管理器

存儲未爬取的URL和爬取過的URLpython

做用

  • 防止重複抓取。爬取事後的URL存在已爬取URL集合中,再也不去訪問
  • 防止循環抓取。若是A網頁包含B網頁的連接,B網頁包含A網頁的連接,可能致使無限循環。每次添加URL時,判斷未爬取URL集合、已爬取URL集合能夠避免

須要實現的功能

  • 能夠添加新的URL到未爬取集合中,該功能須要判斷新的URL是否在URL管理器容器中
  • 判斷是否還有未爬取的URL
  • 獲取一個待爬取的URL,該功能須要從未爬取的URL集合中刪除該URL,並添加到已爬取的URL集合中

實現方式

  • 使用Python,用兩個Set分別表示
  • 使用Mysql等數據庫存儲
  • 使用Redis等,也可使用兩個Set分別表示

網頁下載器

將互聯網上URL對應的網頁下載到本地的工具,本文介紹Python自帶庫「urllib2」正則表達式

"urllib2"使用方法

  1. 最簡單的使用方法sql

    pic

  2. 添加data、http header數據庫

    pic

  3. 添加特殊情景的處理器(HTTPCookieProcessor、ProxyHandler、HTTPSHandler、HTTPRedirectHandler)服務器

    以添加HTTPCookieProcessor爲例:網絡

    pic

網頁解析器

網頁解析器是從網頁中提取有價值數據的工具。架構

pic

常見的幾種網頁解析器

  • 正則表達式:能夠模糊匹配網頁字符串裏中的內容,在比較複雜的數據中很是麻煩
  • html.parser:Python自帶的解析器
  • lxml:第三方插件,能夠解析html和xml
  • Beautiful:第三方插件,它既可使用html.parser做爲解析器,也可使用lxml做爲解析器,功能比較強大,推薦使用

其中,正則表達式是基於文本的模糊匹配;其餘3種方式將網頁文檔解析爲DOM樹解析。app

pic

Beautiful Soup介紹

官網:https://www.crummy.com/software/BeautifulSoup/

pic

對Beautiful Soup進行操做的通常步驟

  1. 先根據HTML網頁字符串建立Beautiful Soup對象。
  2. 獲取節點,能夠經過節點名稱、屬性值、文字搜索節點。find_all會搜索全部知足要求的節點,find搜索出知足要求的第一個節點。
  3. 訪問節點,獲取節點的名稱、屬性、文字的值。
    pic

函數介紹

  1. 建立Beautiful Soup對象

    pic

  2. 搜索節點(find_all,find)

    pic

  3. 訪問節點信息

    pic

爬蟲實例

分析

  1. 肯定目標

    也就是說:咱們須要扒取哪一個網站和哪些數據。

    本實例咱們肯定要抓取百度百科「python」詞條這個頁面和它相關的頁面,數據包括頁面內的關鍵詞、簡介。

  2. 分析目標

    • URL格式,用來限定抓取頁面的範圍

    本實例抓取的URL是頁面中的其餘詞條的超連接,格式相似於<a href="/item/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%AD%E8%A8%80">計算機程序設計語言</a>

    • 抓取的數據格式,用於存儲數據

    本實例都是字符串格式的數據

    • 肯定編碼,確保程序中的編碼格式

    百度百科網頁使用utf-8編碼,因此在編寫代碼中注意轉碼

  3. 編寫代碼
  4. 執行爬蟲

編碼

主模塊

# -*- coding:utf-8 -*-

import url_manager
import html_downloader
import html_parser
import html_outputer


class SpiderMain(object):

    def __init__(self):
        self.urls = url_manager.UrlManager() # 初始化URL管理器
        self.downloader = html_downloader.HtmlDownloader() # 初始化網頁下載器
        self.parser = html_parser.HtmlParser() # 初始化網頁解析器
        self.outputer = html_outputer.HtmlOutputer() # 初始化數據輸出器

    def craw(self,root_url):
        count = 1  # 記錄成功條數
        self.urls.add_new_url(root_url) # 添加url到url管理器
        while self.urls.has_new_url():
            try:
                new_url = self.urls.get_new_url() # 獲取未爬過的url
                print 'craw %d : %s' %(count, new_url)
                html_cont = self.downloader.download(new_url) # 根據url下載網頁
                new_urls, new_data = self.parser.parse(new_url, html_cont) # 解析網頁內容
                self.urls.add_new_urls(new_urls) # 將爬到的新網址添加到url管理器
                self.outputer.collect_data(new_data) # 將內容添加到數據輸出器

                if count == 1000:
                    break

                count = count + 1
            except:
                print 'craw failed'


        self.outputer.output_html() # 輸出內容

# main函數
if __name__ == '__main__':
    root_url = 'https://baike.baidu.com/item/Python' # 最開始的url
    obj_spider = SpiderMain()
    obj_spider.craw(root_url)

URL管理器

# -*- coding:utf-8 -*-

# URL管理器
class UrlManager(object):

    def __init__(self):
        self.new_urls = set() # 存放未讀取url
        self.old_urls = set() # 存放已讀取url

    # 添加一個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
        for url in urls:
            self.add_new_url(url)

    # 返回是否有未讀url
    def has_new_url(self):
        return len(self.new_urls) != 0

    # 隨機獲取一個未讀url
    def get_new_url(self):
        new_url = self.new_urls.pop() # 獲取未讀url並刪除
        self.old_urls.add(new_url) # 添加這個url到已讀url列表中
        return new_url

網頁下載器

# -*- coding:utf-8 -*-

import urllib2

# 網頁下載器
class HtmlDownloader(object):

    # 獲取服務器響應內容
    def download(self,url):
        if url is None:
            return None

        response = urllib2.urlopen(url)

        if response.getcode() != 200: # 獲取成功標識
            return None

        return response.read()

網頁解析器

# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup
import re

# 網頁解析器
class HtmlParser(object):

    # 獲取其中的url
    def __get_new_urls(self,page_url, soup):
        new_urls = set()
        links = soup.find_all('a', href=re.compile(r'/item/[a-zA-Z0-9%]+')) # 匹配url,可能會發生變化
        for link in links:
            new_url = link['href'].encode('utf-8') # 獲取url
            new_full_url = 'https://baike.baidu.com'+new_url # 拼接url
            new_urls.add(new_full_url)
        return new_urls

    # 獲取其中的內容(關鍵詞,簡介)
    def __get_new_data(self,page_url,soup):
        res_data = {}

        res_data['url'] = page_url

        # 獲取關鍵詞
        title_node = soup.find('dd', class_ = 'lemmaWgt-lemmaTitle-title').find('h1')
        res_data['title'] = title_node.get_text()

        # 獲取簡介
        summary_node = soup.find('div', class_ = 'lemma-summary')
        res_data['summary'] = summary_node.get_text()

        return res_data

    # 解析htnl字符串
    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')
        new_urls = self.__get_new_urls(page_url, soup)
        new_data = self.__get_new_data(page_url, soup)
        return new_urls, new_data

數據輸出器

# -*- coding:utf-8 -*-

# 數據輸出器
class HtmlOutputer(object):

    def __init__(self):
        self.datas = list()

    # 添加數據到列表中
    def collect_data(self, data):
        if data is None:
            return
        self.datas.append(data)

    # 輸出到html文件中
    def output_html(self):
        fout = open(r'output.html','w')

        fout.write('<html>')
        fout.write('<body>')
        fout.write('<table>')



        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()
相關文章
相關標籤/搜索