版权声明:本文为博主原创文章,转载本站文章请注明作者和出处,请勿用于任何商业用途。 https://blog.csdn.net/wutianxu123/article/details/82560643
16.5 python web服务器
实现读取GET请求,获取web页面,并将其返回呈现。
#-*-coding: utf-8-*-
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
try:
f = open(self.path[1:], 'r')
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(f.read())
f.close()
except IOError:
self.send_error(404,u'没有发现文件: %s' % self.path)
def main():
try:
server = HTTPServer(('', 80), MyHandler)
print u'欢迎访问'
print u'请按ctrl+c退出'
server.serve_forever()
except KeyboardInterrupt:
print u'已退出并关闭了服务器'
server.socket.close()
if __name__ == '__main__':
main()
16.6 web客户端
Web爬虫:通过起始web地址,下载该页面和其它后续链接页面。但仅限于与起始页面有相同域名的页面,否则耗尽整个磁盘空间。
#-*-coding: utf-8-*-
import cStringIO
import formatter
import htmllib
import httplib
import os
import sys
import urllib
import urlparse
class Retriever(object): #任务是从web下载页面
__slots__ = ('url', 'file')
def __init__(self, url):
self.url, self.file = self.get_file(url)
def get_file(self, url, default='index.html'):
u'将指定的URL转储为可用的本地文件'
parsed = urlparse.urlparse(url)
host = parsed.netloc.split('@')[-1].split(':')[0]
filepath = '%s%s' % (host, parsed.path)
if not os.path.splitext(parsed.path)[1]:
filepath = os.path.join(filepath, default)
linkdir = os.path.dirname(filepath)
if not os.path.isdir(linkdir): #要么路径不存在,要么是普通文件
if os.path.exists(linkdir):
os.unlink(linkdir)
os.makedirs(linkdir)
return url, filepath
def download(self):
u'将URL下载到指定的命名文件'
try:
retval = urllib.urlretrieve(self.url, self.file)
except (IOError, httplib.InvalidURL) as e:
retval = ((u'错误!不可用的URL:"%s":%s' % (self.url,e)),)
return retval
def parse_links(self):
u'解析下载下来的HTML文件中是否有额外的链接'
f = open(self.file, 'r')
data = f.read()
f.close()
parser = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(cStringIO.StringIO())))
parser.feed(data)
parser.close()
return parser.anchorlist
class Crawler(object): #管理一个web站点的完整抓爬过程
count = 0
def __init__(self, url):
self.q = [url] #待下载链接集合
self.seen = set() #已下载链接集合
parsed = urlparse.urlparse(url)
host = parsed.netloc.split('@')[-1].split(':')[0]
self.dom = '.'.join(host.split('.')[-2:]) #存储主链接的域名
def get_page(self, url, media=False):
u'下载页面和分析链接'
r = Retriever(url)
fname = r.download()[0]
if fname[0] == '*':
print fname, u'跳过解析'
return
Crawler.count += 1
print '\n(', Crawler.count, ')'
print 'URL:', url
print 'FILE:', fname
self.seen.add(url)
ftype = os.path.splitext(fname)[1]
if ftype not in ('.htm', '.html'):
return
for link in r.parse_links():
if link.startswith('mailto:'):
print u'丢弃!无效连接'
continue
if not media:
ftype = os.path.splitext(link)[1]
if ftype in ('.mp3', '.mp4', '.m4v', '.wav'):
print u'不可用的媒体文件'
continue
if not link.startswith('http://'):
link = urlparse.urljoin(url, link)
print '*', link,
if link not in self.seen:
if self.dom not in link:
print u'丢弃!不在域名中'
else:
if link not in self.q:
self.q.append(link)
print u'新的!添加到下载'
else:
print u'丢弃!已经下载过了'
else:
print u'丢弃!已经处理过了'
def go(self, media=False):
u'处理队列中的下一页(如果有的话)'
while self.q:
url = self.q.pop()
self.get_page(url, media)
def main():
if len(sys.argv) > 1:
url = sys.argv[1]
else:
try:
url = raw_input(u'输入起始URL: '.encode('gb2312'))
except (KeyboardInterrupt, EOFError):
url = ''
if not url:
return
if not url.startswith('http://') and not url.startswith('ftp://'):
url = 'http://%s/' % url
robot = Crawler(url)
robot.go()
if __name__ == '__main__':
main()
解析web页面:给定一个URL后,脚本会提取所有链接,尝试进行必要的调整,生成完整的URL,对这些URL进行排序并输出。这段脚本会使用2个不同的解释器来从HTML标签中提取链接,分别是:HTMLParser、BeautifulSoup。
#-*-coding: utf-8-*-
from HTMLParser import HTMLParser
from cStringIO import StringIO
from urllib2 import urlopen
from urlparse import urljoin
#顺序:先导入标准模块/包(以上),后导入第三方模块/包(以下)
from BeautifulSoup import BeautifulSoup,SoupStrainer
URLS = ('http://www.baidu.com',) #可以自由添加、移除。注意必须有逗号!
def output(x):
print '\n'.join(sorted(set(x))) #按字母顺序排序呈现出来
def simpleBS(url, f):
u'simpleBS()函数-使用BeautifulSoup解析所有的标签来获得锚'
output(urljoin(url,x['href']) for x in BeautifulSoup(f).findAll('a'))
def fasterBS(url, f):
u'fasterBS()函数-使用BeautifulSoup解析过滤,只处理含有锚的标签'
output(urljoin(url,x['href']) for x in BeautifulSoup(f,parseOnlyThese=SoupStrainer('a')))
def htmlparser(url, f):
u'htmlparser()函数-使用htmlparser解析锚标签'
class AnchorParser(HTMLParser):
def handle_starttag(self, tag, attrs):
if tag != 'a':
return
if not hasattr(self, 'data'):
self.data = []
for attr in attrs:
if attr[0] == 'href':
self.data.append(attr[1])
parser = AnchorParser()
parser.feed(f.read())
output(urljoin(url,x) for x in parser.data)
def process(url, data):
print u'\n***simpleBS()函数解析结果-所有标签'
simpleBS(url, data)
data.seek(0)
print u'\n***fasterBS()函数解析结果-只含有锚的标签'
fasterBS(url, data)
data.seek(0)
print u'\n***htmlparser()函数解析结果-解析锚标签'
htmlparser(url, data)
data.seek(0)
def main():
for url in URLS:
f = urlopen(url)
data = StringIO(f.read())
f.close()
process(url, data)
if __name__ == '__main__':
main()
可编程的web浏览:一款批处理类型的脚本。该脚本分为7个部分,每部分浏览特定站点的一个页面。使用Mechanize浏览web站点,使用BeautifulSoup来进行解析。
注:可能是因为网络的原因,也可能是代码的原因,该程序未成功运行。
#-*-coding: utf-8-*-
from BeautifulSoup import BeautifulSoup as BS
from mechanize import Browser
br = Browser()
#访问站点主页面
rsp = br.open('http://us.pycon.org/2011/home/')
print '\n***', rsp.geturl()
print u"确认主页有“登录”链接并单击它"
page = rsp.read()
assert 'Log in' in page, 'Log in not in page' #断言函数。以布尔值为判断,当我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃
rsp = br.follow_link(text_regex='Log in')
#访问站点登录页面
print '\n***', rsp.geturl()
print u'确认至少提交一个有效的登录表单'
assert len(list(br.forms())) > 1, 'no forms on this page'
br.select_form(nr=0)
br.form['username'] = 'admin1' #错误的用户名
br.form['password'] = 'password1' #错误的密码
rsp = br.submit()
#站点登录错误页面,需重新提交
print '\n***', rsp.geturl()
print u'由于登录凭据无效,请提交有效登录凭据'
assert rsp.geturl() == 'http://us.pycon.org/2011/account/login/', rsp.geturl()
page = rsp.read()
err = str(BS(page).find("div",{"id": "errorMsg"}).find('ul').find('li').string)
assert err == u'您的用户名或密码不正确', err
br.select_form(nr=0)
br.form['username'] = 'admin' #正确的用户名
br.form['password'] = 'password' #正确的密码
rsp = br.submit()
#登录成功,返回主页面
print '\n***', rsp.geturl()
print u'恭喜您!登录成功!'
assert rsp.geturl() == 'http://us.pycon.org/2011/home/', rsp.geturl()
page = rsp.read()
assert 'Logout' in page, 'Logout not in page'
rsp = br.follow_link(text_regex='Account')
#注册页面
print '\n***', rsp.geturl()
print u'使用电子邮箱地址进行注册'
assert rsp.geturl() == 'http://us.pycon.org/2011/account/email/', rsp.geturl()
page = rsp.read()
assert 'Email Addresses' in page, 'Missing email addresses'
print 'Primary e-mail: %r' % str(BS(page).find('table').find('tr').find('td').find('b').string)
rsp = br.back()
#返回主页面
print '\n***', rsp.geturl()
print u'返回主页面'
assert rsp.geturl() == 'http://us.pycon.org/2011/home/', rsp.geturl()
rsp = br.follow_link(url_regex='logout')
#退出登录
print '\n***', rsp.geturl()
print u'确认已经退出登录'
assert rsp.geturl() == 'http://us.pycon.org/2011/account/logout/', rsp.geturl()
page = rsp.read()
assert 'Log in' in page, 'Log in not in page'
print '\n*** DONE'