Python爬蟲實戰專案1 | 基礎爬蟲的實現(爬取100條百度百科詞條)
【基礎爬蟲篇】
本篇講解一個比較簡單的Python爬蟲。
這個爬蟲雖然簡單,但五臟俱全,大爬蟲有的模組這個基礎爬蟲都有,只不過大爬蟲做的更全面、多樣。
1.實現的功能:這個爬蟲實現的功能為爬取百度百科中的詞條資訊。爬取的結果見6。
2.背景知識:(1).Python語法;(2).BeautifulSoup;(3).HTML知識;
Python基礎語法學習可參看:推薦《Python程式設計——從入門到實踐》,或者廖雪峰Python部落格。
BeautifulSoup主要語法可參看:BeautifulSoup主要知識點
3.基礎爬蟲框架及執行流程:
基礎爬蟲包括五個模組,分別是爬蟲排程器,URL管理器,HTML下載器,HTML解析器,資料儲存器。
功能分析如下:
(1).爬蟲排程器主要負責統籌其他四個模組的協調工作;
(2).URL管理器負責管理URL連結,維護已經爬取的URL集合和未爬取的URL集合,提供獲取新URL連結的介面;
(3).HTML下載器用於從URL管理器中獲取未爬取的URL連結並下載HTML網頁;
(4).HTML解析器用於從HTML下載器中獲取已經下載的HTML網頁,並從中解析出新的URL連結交給URL管理器,解析出有效資料交給資料儲存器。
(5).資料儲存器用於將HTML解析器解析出來的資料通過檔案或者資料庫的形式解析出來。
檔案組織目錄:
其中__init__.py的內容為空,它的作用簡單說就是讓這個包變成一個模組,身份發生了變化。
1.URL管理器
連結去重複在Python爬蟲開發中是必備的技能,解決方案主要有三種:1).記憶體去重;2).關係資料庫去重;3).快取資料庫去重;在這個基礎爬蟲中,我使用了Python中的set這種記憶體去重方式,因為資料量比較少。
# url管理器 class UrlManager(object): def __init__(self): self.new_urls = set() self.old_urls = set() 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集合中 :param 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集合中 :param urls: urls:url集合 :return: ''' 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: ''' return len(self.new_urls) def old_url_size(self): ''' 獲取已經爬取的URL集合的大小 :return: ''' return len(self.old_urls)
2.HTML下載器
HTML下載器用來下載網頁,這時候需要注意網頁的編碼,以保證下載的網頁沒有亂碼,下載器用了Requests模組,這個模組比urllib強大很多。
import requests
import chardet
class HtmlDownloader(object):
def download(self, url):
if url is None:
return None
user_agent = 'Mozilla/4.0 (compatible); MSIE 5.5; Windows NT'
headers = {'User-Agent': user_agent}
response = requests.get(url, headers=headers)
if response.status_code == 200:
response.encoding = chardet.detect(response.content)['encoding']
return response.text
else:
print('網頁開啟失敗!')
return None
3.HTML解析器
HTML解析器使用BeautifulSoup進行HTML解析。需要解析的部分主要分為提取相關詞條頁面的URL和提取當前詞條的標題和摘要資訊。
# coding:utf-8
import re
from bs4 import BeautifulSoup
class HtmlParser(object):
def parser(self, page_url, html_cont):
'''
用於解析網頁內容,抽取URL和資料
:param page_url: 下載頁面中的URL
:param html_cont: 下載的網頁內容
:return: 返回URL和資料
'''
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)
# print(new_urls, new_data)
return new_urls, new_data
def __get_new_urls(self, page_url, soup):
'''
抽取新的url集合
:param page_url:下載頁面的url
:param soup: soup
:return: 返回新的url集合
'''
new_urls = set()
# print('new_urls:', new_urls)
# 抽取符合要求的a標記
links = soup.find_all('a', href=re.compile(r'/item/[0-9a-zA-Z%]'))
print(links)
for link in links:
# 提取href屬性
new_url = link.get('href')
# print('new_url:', new_url)
# 拼接成完整網址
new_full_url = 'http://baike.baidu.com' + new_url
# print('new_full_url', new_full_url)
new_urls.add(new_full_url)
# print('new_urls:', new_urls)
return new_urls
def __get_new_data(self, page_url, soup):
'''
抽取有效資料
:param page_url: 下載頁面的URL
:param soup: soup
:return: 返回有效資料
'''
data = {}
data['url'] = page_url
title = soup.find(name='dd', class_='lemmaWgt-lemmaTitle-title').find('h1')
# print(title.text)
data['title'] = title.text
summary = soup.find(name='div', class_='lemma-summary')
# print(summary.text)
data['summary'] = summary.text
return data
4.資料儲存器
資料儲存器主要包括兩個方法:store_data(data)用於將解析出來的資料儲存到記憶體中,output_html()用於將儲存的資料輸出為指定的檔案格式(自己定義)。
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', 'a', encoding='utf-8')
fout.write('<html>')
fout.write('<head><meta charset="utf-8" /></head>')
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()
5.爬蟲排程器
爬蟲排程器用來協調管理這些模組。
# encoding:utf-8
from The_Basic_Spider.DataOutput import DataOutput
from The_Basic_Spider.HtmlDownloader import HtmlDownloader
from The_Basic_Spider.HtmlParser import HtmlParser
from The_Basic_Spider.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()
print(new_url)
# HTML下載器下載網頁
html = self.downloader.download(new_url)
# 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:
print('crawl failed')
# 資料儲存器將檔案輸出成指定格式
self.output.output_html()
if __name__ == '__main__':
spider_man = SpiderMan()
spider_man.crawl('http://baike.baidu.com/view/284853.htm')
然後就可以爬取100條百度百科詞條啦~爬去的資訊將儲存在baike.html裡。
6.爬取結果(baike.html)
當然,你也可以試著爬取自己想爬取的網路資料哦~
如果有幫助,點個喜歡吧~