使用Python的BeautifulSoup庫實現一個可以爬取1000條百度百科數據的爬蟲
阿新 • • 發佈:2018-02-23
otto 提取數據 tps summary 簡介 標題格式 段落 字典 如果 BeautifulSoup模塊介紹和安裝
- BeautifulSoup
- BeautifulSoup是Python的第三方庫,用於從HTML或XML中提取數據,通常用作於網頁的解析器
- BeautifulSoup官網: https://www.crummy.com/software/BeautifulSoup/
- 官網文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc/
- 中文文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
BeautifulSoup安裝很簡單,我們可以直接使用pip來安裝BeautifulSoup,安裝命令如下:
pip install beautifulsoup4
如果使用的IDE是Pycharm的話,安裝更簡單,直接編寫導入模塊的語句:import bs4
,然後會報錯,提示模塊不存在,接著按 alt + 回車,會出現錯誤修正提示,最後選擇安裝模塊即可自動安裝。
安裝完成之後編寫一段測試代碼:
import bs4
print(bs4)
如果執行這段代碼,並且正常輸出沒有報錯則代表已經安裝成功。
BeautifulSoup的語法:
訪問節點信息:
語法格式:
from bs4 import BeautifulSoup import re # 根據HTML網頁字符串內容創建BeautifulSoup對象 soup = BeautifulSoup(html_doc, # HTML文檔字符串 ‘html.parser‘, # HTML解析器 from_encoding=‘utf-8‘ # HTML文檔的編碼,在python3中不需要加上這個參數 ) # 方法:find_all(name, attrs, string) # 查找所有標簽為 a 的節點 soup.find_all(‘a‘) # 查找所有標簽為 a 的節點,並鏈接符合/view/123.html形式的節點 soup.find_all(‘a‘, href=‘/view/123.html‘) soup.find_all(‘a‘, href=re.compile(‘/view/\d+\.html‘)) # 查找所有標簽為div,class為abc,標簽內容為Python的節點 soup.find_all(‘div‘, class_=‘abc‘, string=‘標簽內容為Python的節點‘) # 得到節點:<a href=‘1.html‘>Python</a> # 獲取查找到的節點的標簽名稱 node.name # 獲取查找到的a節點的href屬性 node[‘href‘] # 獲取查找到的a節點的鏈接文字 node.get_text()
實際的測試代碼:
from bs4 import BeautifulSoup import re html_doc = """ <html><head><title>The Dormouse‘s story</title></head> <body> <p class="title"><b>The Dormouse‘s story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """ # 創建BeautifulSoup對象 soup = BeautifulSoup(html_doc, ‘html.parser‘) print("獲取所有的連接") links = soup.find_all(‘a‘) for link in links: print(link.name, link[‘href‘], link.get_text()) print("\n獲取lacie的連接") link_node = soup.find(‘a‘, href=‘http://example.com/lacie‘) print(link_node.name, link_node[‘href‘], link_node.get_text()) print("\n使用正則表達式進行匹配") link_node = soup.find(‘a‘, href=re.compile(r"ill")) print(link_node.name, link_node[‘href‘], link_node.get_text()) print("\n獲取p段落文字") p_node = soup.find(‘p‘, class_="title") print(p_node.name, p_node.get_text())
實例爬蟲
簡單了解了BeautifulSoup並且完成了BeautifulSoup的安裝後,我們就可以開始編寫我們的爬蟲了。
我們編寫一個簡單的爬蟲一般需要完成以下幾個步驟:
-
確定目標
- 確定要爬取的網頁,例如本實例要爬取的是百度百科與Python相關的詞條網頁以及標題和簡介
-
分析目標
- 分析目標網頁的URL格式,避免抓取不相幹的URL
- 分析要抓取的數據格式,例如本實例中要抓取的是標題和簡介等數據
- 分析目標網頁的編碼,不然有可能在使用解析器解析網頁內容時會出現亂碼的情況
-
編寫代碼
- 分析完目標頁面後就是編寫代碼去進行數據的爬取
- 執行爬蟲
- 代碼編寫完成之後,自然是執行這個爬蟲,測試能否正常爬取數據
開始分析本實例需要爬取的目標網頁:
- 目標:百度百科Python詞條相關詞條網頁-標題和簡介
- 入口頁:https://baike.baidu.com/item/Python/407313
- URL格式:
- 詞條頁面URL:/item/name/id 或者 /item/name/,例:/item/C/7252092 或者 /item/Guido%20van%20Rossum
- 數據格式:
- 標題格式:
<dd class="lemmaWgt-lemmaTitle-title"><h1>***</h1>***</dd>
- 簡介格式:
<div class="lemma-summary" label-module="lemmaSummary">***</div>
- 標題格式:
- 頁面編碼:UTF-8
分析完成之後開始編寫實例代碼
- 該爬蟲需要完成的目標:爬取百度百科Python詞條相關1000個頁面數據
首先創建一個工程目錄,並在目錄下創建一個python包,在該包下創建相應的模塊文件,如下圖:
- spider_main:爬蟲調度器程序,也是主入口文件
- url_manager:url管理器,管理並存儲待爬取的url
- html_downloader:下載器,用於下載目標網頁的內容
- html_parser:解析器,解析下載好的網頁內容
- html_outputer:輸出器,將解析後的數據輸出到網頁上或控制臺中
爬蟲調度器程序代碼:
‘‘‘
爬蟲調度器程序,也是主入口文件
‘‘‘
import url_manager, html_downloader, html_parser, 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):
# 記錄當前爬取的是第幾個URL
count = 1
# 將入口頁面的url添加到url管理器裏
self.urls.add_new_url(root_url)
# 啟動爬蟲的循環
while self.urls.has_new_url():
try:
# 獲取待爬取的url
new_url = self.urls.get_new_url()
# 每爬取一個頁面就在控制臺打印一下
print("craw", count, new_url)
# 啟動下載器來下載該url的頁面內容
html_cont = self.downloader.download(new_url)
# 調用解析器解析下載下來的頁面內容,會得到新的url列表及新的數據
new_urls, new_data = self.parser.parse(new_url, html_cont)
# 將新的url列表添加到url管理器裏
self.urls.add_new_urls(new_urls)
# 收集解析出來的數據
self.outputer.collect_data(new_data)
# 當爬取到1000個頁面時則停止爬取
if count == 1000:
break
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管理器,管理並存儲待爬取的url。
url管理器需要維護兩個列表,一個是
待爬取的url列表,另一個是已爬取的
url列表。
‘‘‘
class UrlManager(object):
def __init__(self):
self.new_urls = set() # 待爬取的url列表
self.old_urls = set() # 已爬取的url列表
def add_new_url(self, url):
‘‘‘
向管理器中添加新的url,也就是待爬取的url
:param url: 新的url
:return:
‘‘‘
# url為空則結束
if url is None:
return
# 該url不在兩個列表中才是新的url
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
:param urls: 新的url列表
:return:
‘‘‘
if urls is None or len(urls) == 0:
return
for url in urls:
self.add_new_url(url)
def has_new_url(self):
‘‘‘
判斷管理器中是否有待爬取的url
:return: True 或 False
‘‘‘
return len(self.new_urls) != 0
def get_new_url(self):
‘‘‘
從url管理器中獲取一個待爬取的url
:return: 返回一個待爬取的url
‘‘‘
# 出棧一個url,並將該url添加在已爬取的列表中
new_url = self.new_urls.pop()
self.old_urls.add(new_url)
return new_url
下載器代碼:
‘‘‘
下載器,用於下載目標網頁的內容
‘‘‘
from urllib import request
class HtmlDownloader(object):
def download(self, url):
‘‘‘
下載url地址的頁面內容
:param url: 需要下載的url
:return: 返回None或者頁面內容
‘‘‘
if url is None:
return None
response = request.urlopen(url)
if response.getcode() != 200:
return None
return response.read()
解析器代碼:
‘‘‘
解析器,解析下載好的網頁內容
‘‘‘
import re
import urllib.parse
from bs4 import BeautifulSoup
class HtmlParser(object):
def parse(self, page_url, html_cont):
‘‘‘
解析下載好的網頁內容
: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‘)
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列表
:param page_url:
:param soup:
:return:
‘‘‘
new_urls = set()
# 詞條頁面URL:/item/name/id 或者 /item/name/,例:/item/C/7252092 或者 /item/Guido%20van%20Rossum
links = soup.find_all(‘a‘, href=re.compile(r"/item/(.*)"))
for link in links:
new_url = link[‘href‘]
# 拼接成完整的url
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):
‘‘‘
解析數據,並返回解析後的數據
:param page_url:
:param soup:
:return:
‘‘‘
# 使用字典來存放解析後的數據
res_data = {}
# url
res_data[‘url‘] = page_url
# 標題標簽格式:<dd class="lemmaWgt-lemmaTitle-title"><h1>***</h1>***</dd>
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">***</div>
summary_node = soup.find(‘div‘, class_=‘lemma-summary‘)
res_data[‘summary‘] = summary_node.get_text()
return res_data
輸出器代碼:
‘‘‘
輸出器,將解析後的數據輸出到網頁上
‘‘‘
class HtmlOutputer(object):
def __init__(self):
# 存儲解析後的數據
self.datas = []
def collect_data(self, data):
‘‘‘
收集數據
:param data:
:return:
‘‘‘
if data is None:
return
self.datas.append(data)
def output_html(self):
‘‘‘
將收集的數據以html的格式輸出到html文件中,我這裏使用了Bootstrap
:return:
‘‘‘
fout = open(‘output.html‘, ‘w‘, encoding=‘utf-8‘)
fout.write("<!DOCTYPE html>")
fout.write("<html>")
fout.write(‘<head>‘)
fout.write(‘<meta charset="UTF-8" />‘)
fout.write(
‘<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"/>‘)
fout.write(‘</head>‘)
fout.write("<body>")
fout.write(
‘<div style="width: 1000px;margin: auto" class="bs-example" data-example-id="bordered-table" ><table class="table table-bordered table-striped" >‘)
fout.write(
‘<thead><tr style="height: 70px;font-size: 20px"><th style="text-align: center;vertical-align: middle;width: 60px">#</th><th style="text-align: center;vertical-align: middle;width: 150px">URL & 標題</th><th style="text-align: center;vertical-align: middle;">簡介</th></tr></thead><tbody>‘)
num = 0
for data in self.datas:
fout.write("<tr>")
fout.write("<th style=‘text-align: center;vertical-align: middle;‘ scope=‘row‘>%d</th>" % num)
fout.write("<td style=‘text-align: center;vertical-align: middle;‘><a href=%s>%s</a></td>" % (
data[‘url‘], data[‘title‘]))
fout.write("<td>%s</td>" % data[‘summary‘])
fout.write("</tr>")
num += 1
fout.write("</tbody></table></div>")
fout.write("</body>")
fout.write("</html>")
fout.close()
運行效果:
控制臺輸出:
生成的html文件:
至此,我們一個簡單的爬蟲就完成了。
源碼GitHub地址:
https://github.com/Binary-ZeroOne/easy-spider
使用Python的BeautifulSoup庫實現一個可以爬取1000條百度百科數據的爬蟲