WEB 是 Python 中的一大方向,涉及的内容很广,这里只尝试三个主题:屏幕抓取、CGI和 WEB 框架。

屏幕抓取

屏幕抓取是通过程序下载网页并从中提取信息的过程。
整个流程可以归纳为下载数据并对其进行分析,可以使用 urllib 来获取网页的 HTML 代码,然后使用正则表达式来匹配所需信息。

urllib 与 re 获取网页数据的示例:

1
2
3
4
5
6
7
8
from urllib.request import urlopen
import re

p = re.compile('<a href="(/jobs/\\d+)/">(.*?)</a>')
text = urlopen('http://python.org/jobs').read().decode()

for url, name in p.findall(text):
print('{} ({})'.format(name, url))

使用此类方式抓取屏幕有以下缺点:

  • 正则表达式不容易理解
  • 无法处理独特的 HTML 内容,如 HTML 字符实体

Tidy 和 XHTML 解析

如果每个网页包含的 XHTML 都正确有效,解析工作将变得简单,如果存在标签缺失的情况下就不好处理了。

Tidy

Tidy 用于对格式不正确且不严谨的 HTML 进行修复的工具。

获取 Tidy

获取 Tidy 的方式有多种,如果使用 UNIX 系统,可以通过软件包管理器安装打包好的软件,如,在 Fedora 中可以通过 dnf install -y tidy.x86_64 进行安装,也可以通过 pip 进行安装,如 pip install pytidylib 。
实现 tidy 的 Python 包有很多,也可以选择安全其它包。

为何使用 XHTML

XHTML 相对于 HTML 的主要区别在于 XHTML 非常严格,要求显示的结束所有的元素.

使用 HTMLParser

使用 HTMLParser 意味着继承它,并重写各种事件处理方法。
下面列出了相关的方法以及解析器在什么时候自动调用他们。

回调方法 何时被调用
handle_starttag(tag, attrs) 遇到开始标签时调用
handle_startendtag(tag, attrs) 遇到空标签时调用
handle_endtag(tag) 遇到结束标签时调用
handle_data(data) 遇到文本数据时调用
handle_charref(ref) 遇到形如&#ref;的字符引用时调用
handle_entityref(name) 遇到形如&name;的实体引用时调用
handle_comment(data) 遇到注释时;只对注释内容调用
handle_decl(decl) 遇到形如的声明时调用
handle_pi(data) 用于处理指令
unknown_decl(data) 遇到未知声明时调用

使用模块 HTMLParser 的屏幕抓取示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from urllib.request import urlopen
from html.parser import HTMLParser

def isjob(url):
try:
a, b, c, d = url.split('/')
except ValueError:
return False

return a == d == '' and b == 'jobs' and c.isdigit()

class Scraper(HTMLParser):
in_link = False

def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
url = attrs.get('href', '')
if tag == 'a' and isjob(url):
self.url = url
self.in_link = True
self.chunks = []

def handle_data(self, data: str):
if self.in_link:
self.chunks.append(data)

def handle_endtag(self, tag: str):
if tag == 'a' and self.in_link:
print('{} ({})'.format(''.join(self.chunks), self.url))
self.in_link = False

text = urlopen('http://python.org/jobs').read().decode()
parser = Scraper()
parser.feed(text)
parser.close()

Beautiful Soup

Beautiful Soup 是一个小巧出色的模块,用于解析在 Web 上可能遇到的不严谨且格式糟糕的 HTML。

通过 pip install beautifulsoup4 进行安装。
使用 Beautiful Soup 的屏幕抓取示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from urllib.request import urlopen
from bs4 import BeautifulSoup

text = urlopen('http://python.org/jobs').read()
soup = BeautifulSoup(text, 'html.parser')

jobs = set()
for job in soup.body.select('h2'):
try:
jobs.add('{} ({})'.format(job.a.string, job.a['href']))
except AttributeError:
pass

print('\n'.join(sorted(jobs, key=str.lower)))

使用 CGI 创建动态网页

通用网关接口,是一种标准机制,Web 服务器可通过它将查询交给专用程序,并以网页的方式显示查询结果。

第一步:准备 Web 服务器

可通过 Python 中的 http.server 模块,直接运行一个临时的 Web 服务器。如果指定了 --cgi 选项,启动的服务器将支持 CGI 。

1
2
(venv) [hcai@P1-Gen4 Chapter_15]$ python -m http.server --cgi
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

CGI 程序必须放置在可通过 Web 访问的目录中,常见的方式:

  • 将脚本放在子目录 cgi-bin 中
  • 将脚本文件的后缀名指定为 .cgi

第二步:添加 !# 行

将脚本放置到正确位置后,必须在开头添加一个 !# 的行,没有的话,Web 服务器将不知道如何执行脚本。
通常只需在脚本开头添加以下行即可:
!#/usr/bin/env python

第三步:设置文件权限

必须确保脚本能够被读取和执行的权限,可以设置为 755 。

简单的 CGI 脚本示例:

1
2
3
4
5
6
#!/home/hcai/pythonProject/Chapter_15/venv/bin/python

print('Context-type: text/html')
print()

print('<h1>Hello World!</h1>')

使用 cgitb 进行调试

如果 CGI 脚本出现错误,Web 页面没有任何提示,为了能方便捕获异常显示堆栈信息,可以启动 cgitb 模块。

显示栈跟踪的 CGI 脚本:

1
2
3
4
5
6
7
8
#!/home/hcai/pythonProject/Chapter_15/venv/bin/python

import cgitb; cgitb.enable()

print('Content-type: text/html\n')

print(1/0)
print('<h1>Hello World!</h1>')

浏览器访问后显示的栈信息:

使用模块 cgi

在 CGI 脚本中,可以通过 cgi 模块中的 FieldStorage 类来获取输入的字段。

示例一:

1
2
3
4
5
6
7
8
#!/home/hcai/pythonProject/Chapter_15/venv/bin/python

import cgi
form = cgi.FieldStorage()

name = form.getvalue('name', 'World')
print('Content-type: text/plain\n')
print('Hello, {}!'.format(name))

示例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/home/hcai/pythonProject/Chapter_15/venv/bin/python

import cgi
form = cgi.FieldStorage()
name = form.getvalue('name', 'world')

print("""Content-type: text/html

<html>
<head>
<title>Greeting Page</title>
</head>
<body>
<h1>Hello, {}!</h1>
<form action='simple4.cgi'>
Change name <input type='text' name='name' />
<input type='submit' />
</form>
</body>
</html>
""".format(name))

使用 Web 框架

Web 框架可以帮助完成繁琐的工作,常用的 Web 框架有:

名称 网站
Django https://djangoproject.com
TurboGears http://turbogears.org
web2py http://web2py.com
Grok https://pypi.python.org/pypi/grok
Zope2 https://pypi.python.org/pypi/Zope2
Pyramid https://trypyramid.com

Flask 框架使用的简单示例:

1
2
3
4
5
6
from flask import Flask
app = Flask(__name__)

@app.route('/')
def powers(n=10):
return ', '.join(str(2**i) for i in range(n))