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应用。