1. 程式人生 > >Python基礎爬蟲

Python基礎爬蟲

數據存儲 有效 ati info 面向 鏈接 添加 itl blog

搭建環境:

win10,Python3.6,pycharm,未設虛擬環境

之前寫的爬蟲並沒有架構的思想,且不具備面向對象的特征,現在寫一個基礎爬蟲架構,爬取百度百科,首先介紹一下基礎爬蟲框架的五大模塊功能,包括爬蟲調度器,URL管理器,HTML下載器,HTML解析器,數據存儲器,功能分析如下:

  >>爬蟲調度器主要負責統籌其他四個模塊的協調工作

  >>URL管理器負責管理URL鏈接,維護已經爬取的URL集合和未爬取的URL集合,提供獲取新URL鏈接的接口

  >>HTML下載器用於從URL管理器中獲取未爬取的URL鏈接並下載HTML網頁

  >>HTML解析器用於從HTML下載器中獲取已經下載的HTML網頁,並從中解析出新的URL鏈接交給URL管理器,解析出有效數據交給數據存儲器

  >>數據存儲器用於將HTML解析器解析出來的數據通過文件或者數據庫的形式存儲起來

URL管理器:

URL管理器主要包括兩個變量,一個是已爬取的URL集合,另一個是未爬取的URL集合;鏈接去重很重要,因為爬取鏈接重復時容易造成死循環,防止鏈接重復方法主要有三種,一是內存去重,二是關系數據庫去重,三是緩存數據庫去重;大型成熟的爬蟲基本上采用緩存數據庫的去重方案,盡可能避免內存大小的限制,又比關系型數據庫去重性能高得多(每爬一個鏈接之前都要在數據庫中查詢一遍);由於基礎爬蟲的爬取數量較小,因此我們使用Python中set這個內存去重方式

在pycharm中新建一個python項目,然後新建一個URLManager.py文件,敲入以下代碼:

class UrlManager(object):
    def __init__(self):
        self.new_urls = set()#未爬取URL集合
        self.old_urls = set()#已爬取URL集合

    def has_new_url(self):
        ‘‘‘
        判斷是否有未爬取的url
        :return
        ‘‘‘
        return self.new_url_size()!= 0

    def get_new_url(self):
        ‘‘‘
        獲取一個未爬取的url
        :return:
        
‘‘‘ new_url = self.new_urls.pop() self.old_urls.add(new_url) return new_url def add_new_url(self,url): ‘‘‘ 將新的url添加到未爬取的URL集合中 :return: ‘‘‘ if url is None: return if url not in self.new_urls and url not in self.old_urls: self.new_urls.add(url) def add_new_urls(self,urls): ‘‘‘ 將新的URL添加到未爬取的URL集合中 ‘‘‘ if urls is None or len(urls)==0: return for url in urls: self.add_new_url(url) def new_url_size(self): ‘‘‘ 獲取未爬取URL集合的大小 ‘‘‘ return len(self.new_urls) def old_url_size(self): ‘‘‘ 獲取已經爬取URL集合的大小 ‘‘‘ return len(self.old_urls)

HTML下載器

HTML下載器用來下載網頁,這時候需要註意網頁的編碼,以保證下載的網頁沒有亂碼,同樣新建一個HtmlDownloader.py

import requests

class HtmlDownloader(object):

    def download(self,url):
        if url is None:
            return None
        user_agent = Your user_agent
        headers = {User-Agent: user_agent}
        r = requests.get(url,headers=headers)
        if r.status_code==200:
            r.encoding=utf-8
            return r.text
        return None

HTML解析器

在這裏HTML解析器使用BeautifulSoup來解析網頁源碼,其他解析方式還有CSS選擇器,xpath,pyquery(大殺器),正則等等,我們需要提取正文標題,摘要以及網頁中存在的URL鏈接,

同樣新建一個HtmlParser.py文件

看下網頁源碼:

技術分享圖片

定位到了標題位置,div > h1

所以可以這麽寫:

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

再看摘要位置:

技術分享圖片

所以可以這麽寫:

summary = soup.find(div,class_=lemma-summary).get_text().strip()

再看網頁中的URL鏈接:

技術分享圖片

大多數是這種格式:<a target="_blank" href="/item/%E4%B8%87%E7%BB%B4%E7%BD%91">萬維網</a>,以及其他格式,因此寫一個如下的提取(其實並不能提取以91結尾的URL,正則太久沒寫忘記了。。):

links = soup.find_all(a,href=re.compile(r/item/[\w\W]*?91))

具體代碼:

import re
import urllib
from bs4 import BeautifulSoup
import requests

class HtmlParser(object):

    def parser(self,page_url,html_cont):
        ‘‘‘
        用於解析網頁內容,抽取URL和數據
        ‘‘‘
        if page_url is None or html_cont is None:
            return
        soup = BeautifulSoup(html_cont,html5lib)
        new_urls = self._get_new_urls(page_url,soup)
        new_data = self._get_new_data(page_url,soup)
        return new_urls,new_data

    def _get_new_urls(self,page_url,soup):
        ‘‘‘
        抽取新的URL集合
        ‘‘‘
        new_urls = set()
        links = soup.find_all(a,href=re.compile(r/item/[\w\W]*?91))
        for link in links:
            new_url = link[href]
            new_full_url= urllib.parse.urljoin(page_url,new_url)
            new_urls.add(new_full_url)
        return new_urls

    def _get_new_data(self,page_url,soup):
        ‘‘‘
        抽取有效數據
        ‘‘‘
        data = {}
        data[url]  =page_url
        title = soup.find(dd,class_=lemmaWgt-lemmaTitle-title).find(h1)
        data[title] = title.get_text()
        summary = soup.find(div,class_=lemma-summary)
        data[summary]=summary.get_text().strip()

        return data
‘‘‘
以下代碼是我用來單獨測試這個模塊的
def download(self,page_url): if page_url is None: return None user_agent = ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36‘ headers = {‘User-Agent‘: user_agent} r = requests.get(page_url,headers=headers) if r.status_code==200: r.encoding=‘utf-8‘ return r.text return None parser = HtmlParser() page_url = ‘https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711‘ html_cont = parser.download(page_url) new_urls,new_data = parser.parser(page_url,html_cont) print(new_urls,new_data) ‘‘‘

數據存儲器

包括兩個方法,store_data用來將HTML解析模塊解析出來的數據存儲到內存中(list),out_html用來將存儲的數據輸出為HTML格式(利於展示),同樣新建一個DataOutput.py文件

代碼如下:

import codecs

class DataOutput(object):

    def __init__(self):
        self.datas = []
    def store_data(self,data):
        if data is None:
            return
        self.datas.append(data)

    def output_html(self):
        fout = codecs.open(baike.html,w,encoding=utf-8)
        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])
            fout.write("<td>%s</td>"%data[summary])
            fout.write("</tr>")
            self.datas.remove(data)
        fout.write("</table>")
        fout.write("</body>")
        fout.write("</html>")
        fout.close()

爬蟲調度器

爬蟲調度器要做的工作就是初始化各個模塊,然後通過一個方法傳入入口URL,按照流程運行各個模塊,同樣新建一個SpiderMan.py文件

代碼如下:

from DataOutput import DataOutput
from HtmlDownloader import HtmlDownloader
from HtmlParser import HtmlParser
from UrlManager import UrlManager

class SpiderMan(object):
    def __init__(self):
        self.manager = UrlManager()
        self.downloader = HtmlDownloader()
        self.parser = HtmlParser()
        self.output = DataOutput()

    def crawl(self,root_url):
        #添加入口url
        self.manager.add_new_url(root_url)
        #判斷url管理器中是否有新的url,同時判斷抓取了多少個url
        while(self.manager.has_new_url() and self.manager.old_url_size() < 100):
            try:
                #從URL管理器獲取新的url
                new_url = self.manager.get_new_url()
                #HTML下載器下載網頁
                html = self.downloader.download(new_url)
                #print(html)
                # #HTML解析器抽取網頁數據
                new_urls,data = self.parser.parser(new_url,html)
                #print(new_urls,data)
                # #將抽取的url添加到URL管理器中
                self.manager.add_new_urls(new_urls)
                # #數據存儲器存儲文件
                self.output.store_data(data)
                print("已經抓取%s個鏈接"%self.manager.old_url_size())
            except Exception as e:
                print("crawl failed")
        self.output.output_html()

if __name__ == "__main__":
    spider_man = SpiderMan()
    spider_man.crawl("https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711")

最後輸出的HTML文件如下:

技術分享圖片

並不是很利於展示。。。再接再厲

Python基礎爬蟲