一個(gè)簡單的網(wǎng)絡(luò)爬蟲

什么是網(wǎng)絡(luò)爬蟲

簡單的說,網(wǎng)絡(luò)爬蟲就是一種自動(dòng)抓去互聯(lián)網(wǎng)上資源的程序。

簡單的網(wǎng)絡(luò)爬蟲

簡單的網(wǎng)絡(luò)爬蟲原理就是使用特定的 url 作為種子,通過一定的規(guī)則去獲取網(wǎng)頁上的所需要的信息和新的 url,并對新的 url 進(jìn)行爬取。

簡單的網(wǎng)絡(luò)爬蟲的架構(gòu)

如下圖,是簡單網(wǎng)絡(luò)爬蟲的主要架構(gòu)。主要分為三部分: url解析器,網(wǎng)頁下載器,網(wǎng)頁解析器。

pp1-簡單的爬蟲架構(gòu).PNG

url 解析器 :負(fù)責(zé)管理待抓取的 url 集合以及抓取的 url 集合。其中包括:防止重復(fù)抓取,防止循環(huán)抓取等。
網(wǎng)頁下載器:將已經(jīng)抓取的 url 對應(yīng)的網(wǎng)頁下載下來,供給網(wǎng)頁解析器使用。
網(wǎng)頁解析器:主要的功能是獲取下載的網(wǎng)頁中的目標(biāo)數(shù)據(jù)以后生成新的url 集合給 url 管理器。

簡單網(wǎng)絡(luò)爬蟲的工作流程

簡單爬蟲的運(yùn)行流程.PNG

寫一個(gè)簡單的網(wǎng)絡(luò)爬蟲

以抓取百度百科中的 python 詞條頁面的超鏈接為例,代碼使用python語言。

url 管理器

url 管理器主要是管理 url 集合,這里使用了 python 的 set() 集合,因?yàn)?set() 里面的不存在相同元素。

class UrlManager(object):
def __init__(self):
    #創(chuàng)建待爬取和已爬取url集合
    self.new_urls = set()
    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)
#判斷是否待爬取集合為空        
def has_new_url(self):
    return len(self.new_urls) != 0
 #從待爬取集合中取出一個(gè)url 
def get_new_url(self):
    new_url = self.new_urls.pop()
    self.old_urls.add(new_url)
    return new_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)

網(wǎng)頁下載器

這里是使用 python 的基礎(chǔ)庫 urllib2.urlopen() 方法下載 url對于網(wǎng)頁。

import urllib2
class HtmlDownloader(object):
def download(self,url):
    if url is None:
        return None
    #直接請求
    response = urllib2.urlopen(url)
    #獲取狀態(tài)碼,返回200代表下載成功
    if response.getcode()!= 200:
        return None;

    return response.read()

網(wǎng)頁解析器

這里使用了python 的庫— BeautifulSoup,其主要的功能是從網(wǎng)頁抓取數(shù)據(jù),之后從抓取到的數(shù)據(jù)找到目標(biāo)數(shù)據(jù)以及新的新的url集合給url管理器。代碼如下:

from bs4 import BeautifulSoup
import re
import urlparse

class HtmlParse(object):
      #使用BeautifulSoup解析網(wǎng)頁下載器下載的網(wǎng)頁數(shù)據(jù)
      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='utf8')
            #獲取新的url集合
            new_urls = self._get_new_urls(page_url,soup)
            #獲取目標(biāo)數(shù)據(jù)
            new_data = self._get_new_data(page_url,soup)
            return new_urls,new_data
        #獲取新的待爬取url
        def _get_new_urls(self, page_url, soup):
            new_urls = set()
            #使用正則表達(dá)式從BeautifulSoup獲取的數(shù)據(jù)中找到新的url
            #頁面的url格式/item/%E8%9C%98%E8%9B%9B/8135707
            #這里的soup.find_all() 可獲取全部符合條件的標(biāo)簽對象
            links = soup.find_all('a',href =re.compile(r"/item/[%A_Z0_9]+"))
            for link in links:
            new_url = link['href']
            #生成完整的的url:http://baike.baidu.com/item/%E8%9C%98%E8%9B%9B/8135707
            new_full_url = urlparse.urljoin(page_url,new_url)
            new_urls.add(new_full_url)
            return new_urls
        #獲取目標(biāo)數(shù)據(jù),這里只是獲取了標(biāo)簽<dd class="lemmaWgt-lemmaTitle-title">和<div class_="lemma-summary">中的內(nèi)容
        def _get_new_data(self, page_url, soup):
            res_data = {}
            #url
            res_data['url'] = page_url
            #<dd class="lemmaWgt-lemmaTitle-title"><h1>Python</h1>
            #這里的soup.find() 將獲取第一個(gè)符合條件的標(biāo)簽對象
            title_node = soup.find('dd',class_="lemmaWgt-lemmaTitle-title").find("h1") 
            res_data["title"] = title_node.getText()
            #<div class="lemma-summary" label-module="lemmaSummary">
            #這里的soup.find() 將獲取第一個(gè)符合條件的標(biāo)簽對象
            summary_node = soup.find("div",class_="lemma-summary")
            res_data["summary"] = summary_node.getText()
            return res_data

數(shù)據(jù)輸出

這里只是將獲取的數(shù)據(jù)輸出到html文件上,當(dāng)然也可以輸出到其他地方如數(shù)據(jù)庫,本地文件,看具體需要了。

 class HtmlOutputer(object):
def __init__(self):
    self.datas = []

def collect_data(self,data):
    if data is None:
        return
    self.datas.append(data)
    
def output_html(self):
    fout = open('output.html','w')
    fout.write("<html>")
    fout.write("<body>")
    fout.write("<table>")
    
    #默認(rèn)是ascii,為了防止中文亂碼,需要轉(zhuǎn)成utf8
    for data in self.datas:
        fout.write("<tr>")
        fout.write("<td>%s</td>" % data['url'])
        fout.write("<td>%s</td>" % data['title'].encode('utf8'))
        fout.write("<td>%s</td>" % data['summary'].encode('utf8'))
        fout.write("</tr>")
        
    fout.write("</table>")
    fout.write("</body>")
    fout.write("</html>")

最后,將所有的類連接起來:

#不要忘記引入其他類
from baike_py import html_downloader, html_outputer, html_parser
from baike_py import url_manager

class SpiderMain(object):
def __init__(self):
    self.urls =url_manager.UrlManager()
    self.downloader = html_downloader.HtmlDownloader()
    self.parser = html_parser.HtmlParse()
    self.outputer = html_outputer.HtmlOutputer()

def craw(self, root_url):
    count = 1
    self.urls.add_new_url(root_url)
    while self.urls.has_new_url():
        try:
            new_url = self.urls.get_new_url()
            print ("craw %d : %s" % (count,new_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)
            #這里只是抓取了1000條url數(shù)據(jù)
            if count == 1000:
                break
            count = count + 1
        except :
            print (“craw failed”)    
    self.outputer.output_html()

if __name__=="__main__":
root_url = "http://baike.baidu.com/item/Python"
obj_spider = SpiderMain()
obj_spider.craw(root_url)

總結(jié)

  • python 語言有很多相關(guān)的庫,用起來很方便,功能也很強(qiáng)大,如 對于url
    下載網(wǎng)頁的方式這里只是最簡單的方法。當(dāng)然,了解了其中的原理用什么語言都是一樣的。
  • 以上只是一個(gè)簡單爬蟲,只是抓取靜態(tài)html上的url和目標(biāo)數(shù)據(jù)。實(shí)際上網(wǎng)頁有很多資源是通過JavaScript 等動(dòng)態(tài)方式顯示出來的,這樣還需要做額外處理;
  • 對于網(wǎng)絡(luò)爬蟲還需要有更多學(xué)習(xí)的地方,如應(yīng)對反爬取的策略,代理訪問,有的網(wǎng)站還需要使用cookie,分布式爬取等等。弱水三千,道阻且長。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容