3、實例一:抓取電驢鏈接(BeautifulSoup解析網頁)
3.1 BeautifulSoup庫介紹
BeautifulSoup是python開源的html解析庫,能夠用來很方便的操縱html的dom元素,製作網頁爬蟲的利器。
3.2 home頁面實現
home頁面位於整個網頁左側的frame中,顯示的是電驢的電子圖書目錄。我們使用BeautifulSoup抓取電驢的電子圖書總目錄的頁面,從中我們分析出電子圖書的種類,然後顯示在home頁面中。
1)首先在verycdhelper下建立logic目錄,與templates位於同一層次,在logic目錄下新增verycdhelper.py,這就是我們實現網頁抓取邏輯的地方。此目錄中建立一個空的__init__.py文件,以便verycdhelper.py可以被外部的文件引用。
2)在verycdhelper.py中定義get_book_categories函數獲取圖書種類:
from bs4 import BeautifulSoup
import urllib2
import re
class book_category:
def __init__(self, href, category):
self.href = href
self.category = category
def get_book_categories():
verycd_home_url = urllib2.urlopen(
'http://www.verycd.com/archives/book/')
verycd_home = BeautifulSoup(verycd_home_url.read())
div_nav = verycd_home.find("div", id="nav")
book_categories = []
for a in div_nav.find_all("a", href=re.compile("book/[a-zA-z]+/$")):
text = a.get_text()
pos = text.find('(')
category = text[0:pos]
book_categories.append(book_category(a['href'], category))
return book_categories
可以看到book_category類收集了書籍種類和此種類書籍的頁面鏈接。verycd_home是一個BeautifulSoup對象,以verycd的書籍總目錄頁面的url爲初始化參數。然後我們在此頁面中找到id=“nav”的<div>頁面元素,此元素中包含了所有的書籍種類鏈接。在此<div>中遍歷所有的<a>元素,元素的鏈接需要匹配book/[a-zA-z]+/$,即以一個或多個字符結尾。在每一個<a>中,我們取得a['href']屬性爲此種類書籍的頁面鏈接。我們分析<a>的文本子元素a.get_text(),取出'('之前的文本即爲書籍種類。
3)我們還需要修改views.py,在home函數中調用以上函數:
from django.http import HttpResponse
from django.template.loader import get_template
from django.shortcuts import render_to_response
from logic.verycdhelper import *
def home(request):
book_categories = get_book_categories()
return render_to_response('home.html',
{'category_list':book_categories})
我們可以看到,傳遞給home.html模板的時候,我們傳遞了一個字典,字典的只有一項,是書籍的種類列表。
4)我們看看home.html模板現在的樣子,是怎樣用到此book_categories的:
<html>
<head>
<h1> VeryCD Book Menu </h1>
<hr>
</head>
<body>
<ul>
{% for cty in category_list %}
<li><a href={{cty.href}} target="display">{{ cty.category }}</a></li>
{% endfor %}
</ul>
</body>
</html>
在<body>部分,遍歷category_list列表,生成一列<li>項目,每項是一個超鏈接,點擊超鏈接會顯示頁面到名爲display的frame中。此<a>顯示的文本是cty.category。
5)運行程序,我們看看效果:
3.3 顯示書籍種類頁面的模板book_page_list.html實現
我們點擊左邊的目錄項,應當在後側frame中顯示此項書籍種類的頁面序號列表,book_page_list.html即是此頁面的模板。
1)首先,我們要從verycd抓取某一書籍種類的頁面,將此頁面的書籍序號列表都分析出來,顯示到book_page_list.html中。
2)在verycdhelper.py中定義get_book_page_list函數,根據書籍類別獲取書籍的頁面序號列表:
class book_page:
def __init__(self, href, page):
self.href = href
self.page = page
def get_book_page_list(category):
verycd_archive_book_str = 'http://www.verycd.com/archives/book/'
book_list_url = urllib2.urlopen(verycd_archive_book_str + category)
book_list = BeautifulSoup(book_list_url.read())
div_cont = book_list.find("div", id="content")
dl_page_list = div_cont.find("dl")
book_page_list = []
for a in dl_page_list.find_all("a"):
book_page_list.append(book_page(a['href'], a.get_text()))
return book_page_list
先打開verycd的書籍種類的序號頁面,然後我們找到id=“content"的<div>,在此<div>中我們找到<dl>,在此<dl>中我們找到所有的<a>,這些<a>標籤就是書籍序號。我們定義了book_page類存儲書籍序號和書籍序號指向的超鏈接。
3)我們修改views.py和urls.py,增加來匹配get_book_page_list.html模板的函數:
views.py:
def book_page_list(request, category):
book_page_list = get_book_page_list(category)
return render_to_response('book_page_list.html',
{'book_page_list':book_page_list,
'category':category,
})
urls.py:
url(r'^archives/book/([a-zA-z]*)/$', book_page_list)
這裏book_page_list函數調用了網頁抓取模塊,將書籍類別和書籍頁面序號列表傳給book_page_list.html模板,url匹配所有以字符結尾,以archives開頭的url。
4)get_book_page_list.html模板:
<html>
<body>
<h1>{{category}}</h1>
<ul>
{% for pg in book_page_list %}
<li><a href={{pg.href}} target="display">{{pg.page}}</a></li>
{% endfor %}
</ul>
</body>
</html>
此模板將book_page_list中的頁面序號鏈接顯示到右側的frame中,其中每一個鏈接指向的頁面也都顯示到右側的名爲display的frame中。5)我們點擊小說目錄,顯示效果如下:
3.4 書籍的書名列表頁book_list.html實現
我們看到,上一節中列出了書籍序號列表,那我們期望點擊序號列表,可以看到所有的書名。比如,點擊No.1-100,我們可以看到1-100的書籍名稱。
1)在verycdhelper.py中,增加抓取頁面的函數,此函數根據書籍類別和頁面序號獲取書籍的列表:
class book_info:
def __init__(self, href, title):
self.href = href
self.title = title
def get_book_list(category, page):
verycd_archive_book_str = 'http://www.verycd.com/archives/book/'
book_list_url = urllib2.urlopen(verycd_archive_book_str + category
+ '/' + page)
book_list = BeautifulSoup(book_list_url.read())
div_res = book_list.find("div", id="resList")
book_list = []
for a in div_res.find_all("a"):
book_list.append(book_info(a['href'], a['title']))
return book_list
我們從verycd的書籍列表頁面找到id="resList"的<div>,在此<div>下找到所有的<a>,此<a>標籤的鏈接和標題就是書籍詳細信息的鏈接以及書名。
2)在views.py和urls.py中增加此book_list.html的調用:
views.py:
def book_list(request, category, page):
book_info_list = get_book_list(category, page)
book_category = '/archives/book/' + category
back_to_list = 'Back To List'
return render_to_response('book_list.html',
{'book_info_list':book_info_list,
'category':book_category,
'back_to_list':back_to_list,
})
我們增加了一項back_to_list,返回到頁面序號列表頁面的鏈接。
urls.py:
url(r'^archives/book/([a-zA-z]*)/(\d{5}.html)/$', book_list)
3)我們增加book_list.html的模板:
<html>
<body>
<ul>
<li><a href={{category}} target="display">{{back_to_list}}</a></li>
{% for book_info in book_info_list %}
<li><a href={{book_info.href}} target="display">{{book_info.title}}</a></li>
{% endfor %}
</ul>
</body>
</html>
這裏我們增加了back_to_list的鏈接,指向書籍類別的序號列表頁面,並且將書名都列出,每個書名對應的詳細信息鏈接會顯示到名爲display的frame中。
4)看一下效果,點解No.1-100後:
3.5 顯示書籍的具體信息book_detail.html的實現
那麼我們最後一步就是顯示書籍的具體信息了,顯示下載鏈接。
1)在verycdhelper.py中增加抓取書籍詳細信息的函數get_book_detail:
class book_detail:
def __init__(self, title, style, info, description, href_list):
self.title = title
self.style = style
self.info = info
self.description = description
self.href_list = href_list
def get_book_detail(book_id):
download_url_str = 'http://www.verycd.gdajie.com/'
url_final = download_url_str + '/topics/' + book_id
html_download = urllib2.urlopen(url_final)
soup_lk = BeautifulSoup(html_download.read())
div_thumb = soup_lk.find("div", class_="thumb120")
title = div_thumb.a['title']
style = div_thumb.a['style']
start = style.find('(')
end = style.find(')')
style = style[start+1:end]
div_info = soup_lk.find("div", class_="info")
info = div_info.strings
div_desc = soup_lk.find("div", class_="description")
description = div_desc.strings
table = soup_lk.find("table", id="emuleFile")
h = table.a.get('href')
html_target = urllib2.urlopen(h)
soup_h = BeautifulSoup(html_target.read())
div = soup_h.find("div", id="detail")
href_list = []
for a in div.find_all('a'):
href_list.append(a['href'])
return book_detail(title, style, info, description, href_list)
可以看到,我們在book_detail中記錄了每本書的書籍名、書籍圖片、書籍簡介、書籍摘要和下載列表。我們首先抓取到下載網站的書籍下載頁面,找到class_="info"的<div>,從<div>中取出簡介,找到class_=“description”的<div>,取出摘要。注意這裏,<div>的屬性class在BeautifulSoup中是以class_表示的。然後我們找到id="emuleFile"的<table>,從中找到下載鏈接頁面,創建一個新的頁面抓取對象soup_h,在此對象中找到id="detail"的<div>,從此頁面中我們找到所有的<a>標籤,即是下載列表。
2)增加views.py和urls.py的函數:
views.py:
def book_detail(request, book_id):
book_detail = get_book_detail(book_id)
return render_to_response('book_detail.html',
{'book_detail':book_detail,
})
urls.py:
url(r'^topics/(\d+)/$', book_detail),
這裏我們匹配的是形如topics/23456/的url
3)增加book_detail.html模板
<html>
<body>
<div>
<h1>{{book_detail.title}}</h1>
<img src={{book_detail.style}}>
</div>
<hr>
<ul>
<li>
{% for line in book_detail.info %}
<p>{{line}}</p>
{% endfor %}
</li>
<li>
{% for line in book_detail.description %}
<p>{{line}}</p>
{% endfor %}
</li>
<hr>
{% for addr in book_detail.href_list %}
<li><a href={{addr}} target="display">{{addr}}</a></li>
{% endfor %}
</ul>
</body>
</html>
我們顯示了標題,並且顯示了圖書的封面圖片,然後對於info和description中的文字進行了分行的顯示,在頁面最後我們列出了下載列表。
4)我們最後來看看效果如下:
右側的頁面滾動到最後,如下:
3.6 小結
到此,我們就建立了一個完整的Django程序,只使用了View和Url這兩個組件,搭建了從電驢上抓取有用信息的網站。這個網站簡單實用,我自己都在使用它。今後,我在學習Django的過程中會逐步用到其他更多的功能,來完善這個網站或創建新的web應用。