文章目录

  • itertools
    • count()
    • cycle()
    • repeat()
    • chain()
    • groupby()
    • 练习 计算圆周率可以根据公式:
    • @contextmanager
    • @closing
  • urllib
    • Get
    • Post
    • Handler
  • XML
    • DOM vs SAX
  • HTMLParser

itertools

Python的内建模块itertools提供了非常有用的用于操作迭代对象的函数
首先,我们看看itertools提供的几个“无限”迭代器:

count()

import itertoolsa = itertools.count(1)#count(1,2)一次累加2
for n in a:if n<20:print(n)else:break
--->1,2,3,4......,19

如果不限制n值,就会打印出自然数序列,根本停不下来,只能按Ctrl+C退出。

cycle()

cycle()会把传入的一个序列无限重复下去:

>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
...     print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
...

同样停不下来。

repeat()

repeat()负责把一个元素无限重复下去,不过如果提供第二个参数就可以限定重复次数

import itertoolsns = itertools.repeat('A', 5)
for n in ns:print(n)
A A A A A

无限序列虽然可以无限迭代下去,但是通常我们会通过takewhile()等函数根据条件判断来截取出一个有限的序列:

>>> natuals = itertools.count(1)
>>> ns = itertools.takewhile(lambda x: x <= 10, natuals)
>>> list(ns)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

itertools提供的几个迭代器操作函数更加有用:

chain()

chain()可以把一组迭代对象串联起来,形成一个更大的迭代器:

for c in itertools.chain('ABC', 'XYZ'):print(c)
# 迭代效果:'A' 'B' 'C' 'X' 'Y' 'Z'

groupby()

groupby()把迭代器中相邻的重复元素挑出来放在一起:

for key, group in itertools.groupby('AAABBBCCAAA'):print(key, list(group))
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']

实际上挑选规则是通过函数完成的,只要作用于函数的两个元素返回的值相等,这两个元素就被认为是在一组的,而函数返回值作为组的key。如果我们要忽略大小写分组,就可以让元素’A’和’a’都返回相同的key:

>>> for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
...     print(key, list(group))
...
A ['A', 'a', 'a']
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']

练习 计算圆周率可以根据公式:

利用Python提供的itertools模块,我们来计算这个序列的前N项和:

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import itertools
def pi(N):sum = 0x = itertools.count(1,2)sq = list(itertools.takewhile(lambda y: y<=2*N-1,x))for n in sq:sum = sum + 4/(((-1)**((n-1)/2))*n)return sum
print(pi(10))
print(pi(100))
print(pi(1000))
print(pi(10000))
assert 3.04 < pi(10) < 3.05
assert 3.13 < pi(100) < 3.14
assert 3.140 < pi(1000) < 3.141
assert 3.1414 < pi(10000) < 3.1415
print('ok')

在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源的一个方法是使用try...finally

try:f = open(r'e:\web\tt\dump.txt', 'r')print(f.read())
finally:if f:f.close()

try...finally非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭,所以上面的代码可以简化为:

with open(r'e:\web\tt\dump.txt', 'r') as f:print(f.read())

并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。
实现上下文管理是通过__enter____exit__这两个方法实现的。例如,下面的class实现了这两个方法:

class Query(object):def __init__(self,name):self.name = namedef __enter__(self):print('start')return selfdef __exit__(self, exc_type, exc_value, traceback):if exc_type:print('Error')else:print('End')def query(self):print('Query info about %s...' % (self.name))
'这样我们就可以把自己写的资源对象用于with语句:'
with Query('Bob') as q:q.query()'运行结果:'
start
Query info about Bob...
End

__exit__(exc_type, exc_value, exc_traceback) :退出与上下文管理器相关的运行时上下文,返回一个布尔值表示是否对发生的异常进行处理。参数表示引起退出操作的异常,如果退出时没有发生异常,则3个参数都为None。如果发生异常,返回
True 表示不处理异常,否则会在退出该方法后重新抛出异常以由 with 语句之外的代码逻辑进行处理。如果该方法内部产生异常,则会取代由 statement-body 中语句产生的异常。要处理异常时,不要显示重新抛出异常,即不能重新抛出通过参数传递进来的异常,只需要将返回值设置为 False 就可以了。之后,上下文管理代码会检测是否 __exit__() 失败来处理异常

@contextmanager

编写__enter____exit__仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法,上面的代码可以改写如下:

from contextlib import contextmanagerclass Query(object):def __init__(self, name):self.name = namedef query(self):print('Query info about %s...' % self.name)@contextmanager
def create_query(name):print('Begin')q = Query(name)yield qprint('End')

@contextmanager这个decorator接受一个generator,用yield语句把with ... as var把变量输出出去,然后,with语句就可以正常地工作了:

with create_query('Bob') as q:q.query()

很多时候,我们希望在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现。例如:

@contextmanager
def tag(name):print("<%s>" % name)yieldprint("</%s>" % name)with tag("h1"):print("hello")print("world")
'上述代码执行结果为:'
<h1>
hello
world
</h1>

代码的执行顺序是:

1.with语句首先执行yield之前的语句,因此打印出<h1>
2.yield调用会执行with语句内部的所有语句,因此打印出hello和world;
3.最后执行yield之后的语句,打印出</h1>
因此,@contextmanager让我们通过编写generator来简化上下文管理。

@closing

如果一个对象没有实现上下文,我们就不能把它用于with语句。这个时候,可以用closing()来把该对象变为上下文对象。例如,用with语句使用urlopen()

from contextlib import closing
from urllib.request import urlopenwith closing(urlopen('https://www.python.org')) as page:for line in page:print(line)

closing也是一个经过@contextmanager装饰的generator,这个generator编写起来其实非常简单:

@contextmanager
def closing(thing):try:yield thingfinally:thing.close()

它的作用就是把任意对象变为上下文对象,并支持with语句。
@contextlib还有一些其他decorator,便于我们编写更简洁的代码。

urllib

urllib提供了一系列用于操作URL的功能。

Get

urllibrequest模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应:
向服务器发送请求,可以设置一些请求信息,如浏览器的版本,可以通过这个来模拟浏览器访问。然后通过返回的数据可以获取访问的状态、Rsponse Headers中的信息、以及页面的代码

例如,对URLhttps://yesno.wtf/api进行抓取,并返回响应:
urllib.request.urlopen()函数可以用于实现对目标url的访问。

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

参数含义:
url: 需要打开的网址
data:Post提交的数据,data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。
timeout:设置网站的访问超时时间
直接用urllib.request模块的urlopen()获取页面,page的数据格式为bytes类型,需要decode()解码,转换成str类型。
cafile、capath、cadefault 参数:用于实现可信任的CA证书的HTTP请求。(基本上很少用)
context参数:实现SSL加密传输。(基本上很少用)
urlopen返回对象提供方法:
read() , readline() ,readlines() , fileno() , close() :对HTTPResponse类型数据进行操作
info():返回HTTPMessage对象,表示远程服务器返回的头信息
getcode():返回Http状态码。如果是http请求,200请求成功完成;404网址未找到
geturl():返回请求的url

from urllib import requestwith request.urlopen('https://www.baidu.com') as f: # 通过urlopen函数向服务器发送请求,上节closing差不多data = f.read() # 读取返回来的数据print('Status:', f.status, f.reason)   # 获取状态值和状态for k, v in f.getheaders():  # 获取Response Headers信息print('%s: %s' % (k, v))print('Data:', data.decode('utf-8')) # 获取源码,这里用utf-8是因为百度首页是utf8编码的

可以看到HTTP响应的头和JSON数据:

Status: 200 OK
Content-Type: application/json; charset=utf-8
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Transfer-Encoding: chunked
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Connection: close
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Status: 200 OK
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Cache-Control: max-age=0, private, must-revalidate
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Access-Control-Allow-Origin: *
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
X-XSS-Protection: 1; mode=block
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
X-Request-Id: 04fd646c-3090-4272-9e09-ea20aab0a77a
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
ETag: "8946c2cbd4f744b0abf9b8ae654a182c"
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
X-Frame-Options: SAMEORIGIN
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
X-Runtime: 0.002289
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
X-Content-Type-Options: nosniff
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Access-Control-Request-Method: *
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Date: Fri, 17 Jul 2020 01:53:48 GMT
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
X-Powered-By: Phusion Passenger 6.0.4
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}
Server: nginx/1.17.3 + Phusion Passenger 6.0.4
Data: {"answer":"yes","forced":false,"image":"https://yesno.wtf/assets/yes/5-64c2804cc48057b94fd0b3eaf323d92c.gif"}

如果我们要想模拟浏览器发送GET请求,就需要使用Request对象,通过往Request对象添加HTTP头,我们就可以把请求伪装成浏览器。例如,模拟iPhone 6去请求豆瓣首页:

from urllib import requestreq = request.Request('http://www.douban.com/')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
with request.urlopen(req) as f:print('Status:', f.status, f.reason)for k, v in f.getheaders():print('%s: %s' % (k, v))print('Data:', f.read().decode('utf-8'))
'这样豆瓣会返回适合iPhone的移动版网页:'
...<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"><meta name="format-detection" content="telephone=no"><link rel="apple-touch-icon" sizes="57x57" href="http://img4.douban.com/pics/cardkit/launcher/57.png" />
...

Post

如果要以POST发送一个请求,只需要把参数data以bytes形式传入。
我们模拟一个微博登录,先读取登录的邮箱和口令,然后按照weibo.cn的登录页的格式以username=xxx&password=xxx的编码传入:
urllib.urlencode():这个函数只能接收key-value pair格式的数据。即只针对dict的,
urllib的文档中的例子:

>>> import urllib
>>> params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
from urllib import request, parseprint('Login to weibo.cn...')
email = input('Email: ')
passwd = input('Password: ')
login_data = parse.urlencode([('username', email),('password', passwd),('entry', 'mweibo'),('client_id', ''),('savestate', '1'),('ec', ''),('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')
])req = request.Request('https://passport.weibo.cn/sso/login')
req.add_header('Origin', 'https://passport.weibo.cn')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')with request.urlopen(req, data=login_data.encode('utf-8')) as f:print('Status:', f.status, f.reason)for k, v in f.getheaders():print('%s: %s' % (k, v))print('Data:', f.read().decode('utf-8'))
'如果登录成功,我们获得的响应如下:'
Status: 200 OK
Server: nginx/1.2.0
...
Set-Cookie: SSOLoginState=1432620126; path=/; domain=weibo.cn
...
Data: {"retcode":20000000,"msg":"","data":{...,"uid":"1658384301"}}
'如果登录失败,我们获得的响应如下:'
...
Data: {"retcode":50011015,"msg":"\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef","data":{"username":"example@python.org","errline":536}}

Handler

如果还需要更复杂的控制,比如通过一个Proxy去访问网站,我们需要利用ProxyHandler来处理,示例代码如下:
urllib.request.ProxyHandler():构建代理Handle,有代理IP
通过urllib.request.build_opener()方法使用这些代理Handler对象

proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.example.com:3128/'})
proxy_auth_handler = urllib.request.ProxyBasicAuthHandler()
proxy_auth_handler.add_password('realm', 'host', 'username', 'password')
# 创建自定义opener对象
opener = urllib.request.build_opener(proxy_handler, proxy_auth_handler)
#不管是opener.open()还是urlopen() 发送请求,都将使用自定义代理。
with opener.open('http://www.example.com/login.html') as f:pass

使用代理IP,这是爬虫/反爬虫的第二大招,通常也是最好用的。
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
所以我们可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。
在urllib.request库中,通过ProxyHandler来设置使用代理服务器

代理的作用:

1.突破自身IP访问限制,访问一些平时不能访问的站点。
2.访问一些单位或团体内部资源:比如使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。
3.提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时,则直接由缓冲区中取出信息,传给用户,以提高访问速度。
4.隐藏真实IP:上网者也可以通过这种方法隐藏自己的IP,免受攻击。对于爬虫来说,我们用代理就是为了隐藏自身IP,防止自身的IP被封锁。

更详细的讲解:https://www.jianshu.com/p/4c3e228940c8
练习
利用urllib读取JSON,然后将JSON解析为Python对象:

# -*- coding: utf-8 -*-
from urllib import request
import json
def fetch_data(url):with request.urlopen(url) as f:return json.loads(f.read().decode('utf-8'))return None
# 测试
URL = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=json'
data = fetch_data(URL)
print(data)
assert data['query']['results']['channel']['location']['city'] == 'Beijing'
print('ok')

现在就以parse模拟POST请求,大致流程:
1、利用data参数构造内容(关键字内容Form data)
2、使用urlopen()打开
3、返回的是JSON数据类型,使用json.loads()转换成字符串类型
4、结果就是搜索词*(或者是获取数据)的释义

from urllib import request, parse, error
# 返回的数据为JSON格式
import json# 定义基本URL
# 注,这里要将https换成http
baseUrl = "https://fanyi.baidu.com/sug"# 防止报错,使用error.URLerror
try:# 使用data参数构造内容,用以存放Form data# 提示用户输入关键字内容kw = input("Input your kw: ")# 使用data存放Form data,Request传入的必须是字典类型# 通过浏览器F12查看 ,发现form data下字典的key为kw,所以data的key为kwdata = {"kw" : kw}# 发送请求的URL为URL bybes类型,所以使用urlencode()转码# 服务器接收字节类型数据,所以要将关键字参数转换data = parse.urlencode(data).encode()print("即将发送的data数据的类型:{0}".format(type(data)))# 打开网页,传入data参数rsp = request.urlopen(baseUrl, data = data)# 读取内容,返回的内容为type类型,需要转换为strjson_data = rsp.read().decode()print("查看返回数据的类型:{0}".format(type(json_data)))# 通过查看F12,知道返回的数据为JSON格式,所以需要转换json_data = json.loads(json_data)# 可以看到,返回的数据结果是字典类型的print("转换后的数据类型:{0}".format(type(json_data)))#print(json_data)# 因为是字典类型,所以可以使用for循环打印结果for i in json_data["data"]:print(i)except error.URLError as e:print(e)
'运行程序,输入“girl”的结果:'
将发送的data数据的类型:<class 'bytes'>
查看返回数据的类型:<class 'str'>
转换后的数据类型:<class 'dict'>
{'k': 'girl', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;'}
{'k': 'girls', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;'}
{'k': 'girlfriend', 'v': 'n. 女朋友; 女情人; (女子的)女伴,女友;'}
{'k': 'girl friend', 'v': ' 未婚妻; 女性朋友;'}
{'k': "Girls' Generation", 'v': ' 少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女')}

XML

XML虽然比JSON复杂,在Web中应用也不如以前多了,不过仍有很多地方在用,所以,有必要了解如何操作XML

DOM vs SAX

操作XML有两种方法:DOM和SAX。DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。SAX是流模式,边读边解析,占用内存小,解析快,缺点是我们需要自己处理事件。
正常情况下,优先考虑SAX,因为DOM实在太占内存。
在Python中使用SAX解析XML非常简洁,通常我们关心的事件是start_elementend_elementchar_data,准备好这3个函数,然后就可以解析xml了。
举个例子,当SAX解析器读到一个节点时:

<a href="/">python</a>

会产生3个事件:
1.start_element事件,在读取<a href="/">时;
2.char_data事件,在读取python时;
3.end_element事件,在读取</a>时。
用代码实验一下:

from xml.parsers.expat import ParserCreateclass DefaultSaxHandler(object):def start_element(self, name, attrs):print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))def end_element(self, name):print('sax:end_element: %s' % name)def char_data(self, text):print('sax:char_data: %s' % text)xml = r'''<?xml version="1.0"?>
<ol><li><a href="/python">Python</a></li><li><a href="/ruby">Ruby</a></li>
</ol>
'''handler = DefaultSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.start_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(xml)
'执行结果:'
sax:start_element: ol, attrs: {}
sax:char_data:sax:char_data:
sax:start_element: li, attrs: {}
sax:start_element: a, attrs: {'href': '/python'}
sax:char_data: Python
sax:end_element: a
sax:end_element: li
sax:char_data:sax:char_data:
sax:start_element: li, attrs: {}
sax:start_element: a, attrs: {'href': '/ruby'}
sax:char_data: Ruby
sax:end_element: a
sax:end_element: li
sax:char_data:sax:end_element: ol

需要注意的是读取一大段字符串时,CharacterDataHandler可能被多次调用,所以需要自己保存起来,在EndElementHandler里面再合并。

除了解析XML外,如何生成XML呢?99%的情况下需要生成的XML结构都是非常简单的,因此,最简单也是最有效的生成XML的方法是拼接字符串:

L = []
L.append(r'<?xml version="1.0"?>')
L.append(r'<root>')
L.append(encode('some & data'))
L.append(r'</root>')
return ''.join(L)

如果要生成复杂的XML呢?建议你不要用XML,改成JSON
练习
请利用SAX编写程序解析Yahoo的XML格式的天气预报,获取天气预报:

https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=xml

参数woeid是城市代码,要查询某个城市代码,可以在weather.yahoo.com搜索城市,浏览器地址栏的URL就包含城市代码。

# -*- coding:utf-8 -*-from xml.parsers.expat import ParserCreate            # 引入xml解析模块
from urllib import request                            # 引入URL请求模块class WeatherSaxHandler(object):                      # 定义一个天气事件处理器weather ={'city':1,'cityname':[], 'forecast':[]}               # 初始化城市city和预报信息forecastdef start_element(self, name, attrs):             # 定义开始标签处理事件if name=='beijing':self.weather['city']='北京'if name == 'city':               # 获取location信息self.weather['cityname'].append(attrs['cityname'])          #获取地区名# 获取forecast信息self.weather['forecast'].append({'state':attrs['stateDetailed'], 'high':attrs['tem2'], 'low':attrs['tem1']})def parseXml(xml_str):                                # 定义xml解析器handler = WeatherSaxHandler()parser = ParserCreate()parser.StartElementHandler = handler.start_elementparser.Parse(xml_str)                             # 解析xml文本print('City'+handler.weather['city'])for (x,y) in zip(handler.weather['cityname'],handler.weather['forecast']):             # 打印天气信息print('Region:'+x)print(y)return handler.weather# 测试:
URL = 'http://flash.weather.com.cn/wmaps/xml/beijing.xml'with request.urlopen(URL, timeout=4) as f:data = f.read()result = parseXml(data.decode('utf-8'))

HTMLParser

如果我们要编写一个搜索引擎,第一步是用爬虫把目标网站的页面抓下来,第二步就是解析该HTML页面,看看里面的内容到底是新闻、图片还是视频。
假设第一步已经完成了,第二步应该如何解析HTML呢?
HTML本质上是XML的子集,但是HTML的语法没有XML那么严格,所以不能用标准的DOM或SAX来解析HTML。
好在Python提供了HTMLParser来非常方便地解析HTML,只需简单几行代码:

from html.parser import HTMLParser
from html.entities import name2codepointclass MyHTMLParser(HTMLParser):def handle_starttag(self, tag, attrs):print('<%s>' % tag)def handle_endtag(self, tag):print('</%s>' % tag)def handle_startendtag(self, tag, attrs):print('<%s/>' % tag)def handle_data(self, data):print(data)def handle_comment(self, data):print('<!--', data, '-->')def handle_entityref(self, name):print('&%s;' % name)def handle_charref(self, name):print('&#%s;' % name)parser = MyHTMLParser()
parser.feed('''<html>
<head></head>
<body>
<!-- test html parser --><p>Some <a href=\"#\">html</a> HTML&nbsp;tutorial...<br>END</p>
</body></html>''')

feed()方法可以多次调用,也就是不一定一次把整个HTML字符串都塞进去,可以一部分一部分塞进去。

特殊字符有两种,一种是英文表示的&nbsp;,一种是数字表示的Ӓ,这两种字符都可以通过Parser解析出来。

python/itertools-contextlib-urllib-XML-HTMLParser相关推荐

  1. Python 标准库之 xml.etree.ElementTree xml解析

    Python 标准库之 xml.etree.ElementTree Python中有多种xml处理API,常用的有xml.dom.*模块.xml.sax.*模块.xml.parser.expat模块和 ...

  2. python 使用sax 解析xml 文件

    这里不是说xml 的所以如果xml 不了解,可以百度大致看下即可, SAX知识了解 SAX (simple API for XML )  有解析器和事件处理器 解析器负责读取XML文档,并向事件处理器 ...

  3. Python核心模块——urllib模块

    2019独角兽企业重金招聘Python工程师标准>>> urllib模块中的方法 1.urllib.urlopen(url[,data[,proxies]]) 打开一个url的方法, ...

  4. Python爬虫之urllib模块2

    Python爬虫之urllib模块2 本文来自网友投稿 作者:PG-55,一个待毕业待就业的二流大学生. 看了一下上一节的反馈,有些同学认为这个没什么意义,也有的同学觉得太简单,关于Beautiful ...

  5. cpythonjava解释xml_详解python使用lxml操作xml格式文件

    python利用lxml读写xml格式的文件 之前在转换数据集格式的时候需要将json转换到xml文件,用lxml包进行操作非常方便. 1. 写xml文件 a) 用etree和objectify fr ...

  6. python爬虫用urllib还是reques,python爬虫中urllib.request和requests有什么区别?

    在学习python爬虫,想要检索request相关内容时,往往会出现urllib.request和requests这两个词,urllib.request和requests都是python爬虫的模块,其 ...

  7. python常用内置模块-Python常用内置模块之xml模块

    xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.从结构上,很像HTML超文本标记语言.但他们被设计的目的是不同的,超文本标记语言被设计用来显示 ...

  8. python常用内置模块-Python常用内置模块之xml模块(详解)

    xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.从结构上,很像HTML超文本标记语言.但他们被设计的目的是不同的,超文本标记语言被设计用来显示 ...

  9. python 之模块之 xml.dom.minidom解析xml

    # -*- coding: cp936 -*- #python 27 #xiaodeng #python 之模块之 xml.dom.minidom解析xml #http://www.cnblogs.c ...

  10. Python网络请求urllib和urllib3详解

    1. 简介 urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了urllib. 而urllib3则是增加了连接池等功 ...

最新文章

  1. 【Google Play】App Bundle 使用详解 ( 简介 | 应用内更新 | 即时更新 | 灵活更新 )
  2. Android 调用分享框
  3. linux申请cache和释放cache,Linux--- 手工释放系统cache
  4. linux mysql 实战_Linux平台MySQL多实例项目实施_MySQL数据库基础与项目实战06
  5. Define a New Server 没有tomcat选项
  6. Django之Form插件
  7. 读书笔记:《小米生态链战地笔记》
  8. [渝粤教育] 中国地质大学 国际贸易实务 复习题 (2)
  9. java jbutton数组_java-JButton需要显示图像数组
  10. 【Java】# Java对图片进行base64编解码
  11. ibeacon UWB GPS 空间四点定位算法
  12. mysql和sqlite3 ios_iOS数据库存储之SQLite3
  13. 解决mkimage command not found - U-Boot images will not be buil
  14. word快捷键被占用
  15. 五金机电行业供应商智慧管理平台解决方案:优化供应链管理,带动企业业绩增长
  16. 更换Ubuntu的软件仓库为国内的镜像
  17. B2B电子商务网站建设怎么做:B2B系统开发流程、语言、架构解答
  18. NetworkX系列教程(2)-graph生成器
  19. [野狐行][辅助开发系列课程][2016/4/27][一重门公开课全集][官方网站已开放]
  20. android杂凑算法,SM3密码杂凑算法分析

热门文章

  1. 论文解读:ChineseBERT: Chinese Pretraining Enhanced by Glyph and Pinyin Information
  2. vue初始化initMethods
  3. cocos creator上架字节跳动(抖音)小游戏注意事项(匿名登录、录屏、分享等踩坑记录)
  4. ajax上传图片的两种方式
  5. Arrays.asList缺陷
  6. 杭电ACM2529题
  7. springboot自定义静态资源代理WebMvcConfigurerAdapter详解和过期后的替代方案WebMvcConfigurer
  8. linux内核4.9系统版本,Linux4.9版本将会有哪些不一样的特性
  9. SpringBoot高校食堂移动预约点餐系统
  10. 玩csol显示计算机内存不足,电脑中玩反恐精英ol2掉线如何解决_为什么玩反恐精英OL老是掉线...