Python进阶 - HTML获取与解析

1 URL的处理

1.1 URL介绍

HTML使用同一资源定位符(Universal Resource Locator:URL)来定位Internet上的HTML文档信息。URL语法定义如下:

protocol://auth/path?query

常用协议有:http、https、ftp、mailto、file、telnet

一种包含授权的URL详细语法如下:

protocol://username@passwd:netloc:port/path/filename?param = value#tag

具体示例:

http://www.w3.org/2015/10/Process-20150914/activities.html
ftp://ftp.test.com/pub/index.txt
../images/logo.jpg
first.html

1.2 URL的解析

python中的urllib.urlparse用于对url进行解析。主要方法有urlparse、urljoin、urlsplit、urlunsplit等。

urlparse将URL分为六元组:

scheme://netloc/path;parameters?query#fragment

注意这里并没有将服务器地址和端口地址进行区分。
在解析URL的时候所有的%转移符都不会被处理。另外除了第一个起始斜线外,分隔符都会被去掉。
urlparse有两个可选参数:
- default_scheme:为不包含协议的url指定协议
- allow_fragments:指示是否可以对地址进行分片。默认为True

1.3 URL拼合

from urllib.parse import *

webURL = "http://alice:[email protected]:80/%7Ealice/python.cgi\
?query = text#sample"

r = urlparse(webURL)
print(r)

# 第一个参数是绝对地址,第二个是相对地址
join1 = urljoin("http://www.zeroc.com", "ice.html")
print(join1)

# 都含有绝对地址,有限采用相对地址中的协议
join2 = urljoin("http://www.python.org", "ftp:www.python.org/fag")
print(join2)

# 当相对url中不含协议时,会将所有字符当做一个路径信息
join3 = urljoin("http://www.python.org", "www.python.org/faq")
print(join3)

# 优先使用相对url地址的服务器地址和路径
join4 = urljoin("http://www.python.org", "http://www.python.com/fag")
print(join4)

1.4 URL分解

有效的格式化,特殊字符得到转换:

r = urlunsplit(urlsplit("http://www.python.org/faq?")) 

1.5 URL的编码与解码

URL中使用的是ASCII字符集,当使用不在字符集中的字符时,就需要进行编码。即使是ASCII字符集中的字符,有些也不能直接使用,常见的情况是不能在URL中使用空格字符。有些字符被称为保留字符,不能在URL中出现,例如斜线/等。ASCII字符集中的编码规则是:在百分号后面加上两个十六进制数字,与其在ASCII字符表中的数值对应。

大部分的标点符号都需要编码,特别是那些保留字符。一般的,对于不在ASCII字符集中的字符,如果不知道是否应该编码,那么最好进行编码。注意当字符有特殊含义的时候不应进行编码

urllib中的编码解码方法如下:

  • quote 对URL进行编码
  • quote_plus 同quote编码,进一步将空格变为+符号
  • unquote 解码
  • unquote_plus 解码,将+变为空格

1.6 中文的编码与解码

from urllib.parse import quote
r = quote("URL编码")
unquote(r)

1.7 参数的编码

urlencode用于将查询参数加工成url所需的格式

urlencode([("keyword1", "value1"), ("keyword2", "value2"), ("keyword3", "keyword3")])  #列表中的元素是有序的
urlencode({"key1":"val1", "key2":"val2", "key3":"val3"})    #集合中的元素是无序的

urlencode还有一个可选的参数,用于对查询参数中的数据进行控制。默认为False,即当查询参数的value为列表的时候,将其整个用quote_plus进行编码,并作为查询的参数。当其为True时,不会编码。

urlencode([("keyword", ("val1", "val2", "val3"))])
urlencode([("keyword", ("val1", "val2", "val3"))], True)

2 获取HTML资源

2.1 使用urlopen和urlretrieve获取HTTP资源

urlopen

可以读取URL资源,并不能对数据进行seek操作。返回值中有一个可以读的handler,从而可以实现对数据的读取。

from urllib.request import *

fp = urlopen("http://www.python.org")
print(fp.read())

op = open("python.html", "wb")    # 从网络获取的数据最好使用二进制方式
n = 0
while True:
    s = fp.read(1024)
    if not s:    # 遇到 EOF 时跳出循环
        break
    op.write(s)
    n = n + len(s)

fp.close()
op.close()

print("retrieved", n, " bytes from", fp.url)

使用代理:

proxies = {"http":"http://www.proxy.com:2137"}
urlopen(url, proxies = proxies)

urlretrieve
直接存储到本地文件。
关于参数 reporthook,用于报告下载进度的一个函数,有三个参数,分别为已获取的文件块的个数、文件块的大小、文件的大小。当文件大小为 -1 时,表明无法获得整个文件的大小,特别是对于ftp流数据而言。

from urllib.request import *


def download(url, filename = ""):
    def reporthook(block_count, block_size, file_size):
        if file_size == -1:
            print("Cann't determine the file size, now retrieved",
                  block_count * block_size)
        else:
            percentage = int((block_count * block_size * 100.0) / file_size)
            if percentage > 100:
                print(" 100% ")
            else:
                print(" % d% % " % (percentage))

    filehandler, m = urlretrieve(url, filename, reporthook = reporthook)
    print("Done!")
    return filehandler

download("http://www.python.org", "D:/index.html")

2.2 自定义资源获取方式

urllib中还有URLopener及其继承类FancyURLopener,它们也可以完成URL资源的读取。实际上urlopen方法就是FacyURLopener的一个实例,并通过调用其实例的open方法来实现的。一般来说,使用FancyURLopener就够了,URLopener类主要针对除http、ftp、file以外的协议。

访问需要简单认证的URL资源

FancyURLopener类中有prompt_user_passwd方法来处理用户名和密码,当访问的资源需要使用简单认证进行访问的情况下,将会调用此方法得到用户名和密码。默认情况下会从控制台读取用户名和密码。我们需要对这个函数进行重载,直接在代码中调用我们事先设定的参数即可。

from urllib.request import *


class myURLopener(FancyURLopener):
    def setAuth(self, user, passwd):
        self.user = user
        self.passwd = passwd

    def prompt_user_passwd(self, host, realm):
        return self.user, self.passwd

myurlopener = myURLopener()
myurlopener.setAuth(" user", " passwd")

op = myurlopener.open(" http://www.secret.com")

构造文件头元信息
有效避开某些URL资源对于访问的限制

from urllib.request import *

opener = FancyURLopener()
opener.addheader("User-Agent", "Mozilla/4.0 (compatible;MSIE 6.0; \
                 windows NT 5.1)")
opener.addheader("Accept", "text/html")
opener.addheader("Connection", "close")
opener.open("http://www.baidu.com")

访问出错的处理方法

HTTP状态码:

  • 301 数据已被删除
  • 302 资源临时重定向
  • 303 建议访问其他URL
  • 307 资源临时性删除
  • 401 错误的请求
  • 404 访问资源不存在

默认情况下对303错误的处理方法:

def http_error_302(self, url, fp, errcode, errmsg, headers, data = None):
    delf.tries += 1
    if self.maxtries and self.tries >= self.maxtries:
        if hasattr(self, "http_error_500"):
            meth = self.http_error_500
        else:
            meth = self.http_error_default
        self.tries = 0
        return meth(url, fp, 500, "Internal Server Error: Redirect Recursion",
                    headers)
    result = self.redirect_internal(url, fp, errcode, errmsg, headers, data)
    self.tries = 0
    return result

自定义处理方法:

from urllib.request import *

class myURLopener(FancyURLopener):
    def setAuth(self, user, passwd):
        self.user = user
        self.passwd = passwd

    def prompt_user_passwd(self, host, realm):
        return self.user, self.passwd

    def http_error_302(self, url, fp, errcode, errmsg, headers, data = None):
        if "location" in headers:
            newurl = headers["location"]
        elif "uri" in headers:
            newurl = headers["uri"]
        else:
            return
        print(url, "==>", newurl)
        return FancyURLopener.http_error_302(self, url, fp, errcode, errmsg,
                                             headers, data)

myurlopener = myURLopener()
myurlopener.setAuth(" user", " passwd")

op = myurlopener.open(" http://www.secret.com")

2.3 使用httplib获取资源

The httplib module has been renamed to http.client in Python 3.

httplib实现了HTTP和HTTPS协议的客户端部分,一般情况下这个模块并不直接使用,而是作为urllib的基础模块。但实际上,此模块还是比较适用于在访问HTTP资源的时候使用,包括GE和POST方法等等。

3 HTML文档解析

获取到资源之后,结下来便是对HTML文档进行处理了。Python提供了HTMLParser模块来解析HTML文档。另外,由于HTML是属于SGML家族的一员,所以亦可以使用sgmllib模块来处理HTML文档。htmpllib是建立在sgmllib模块上的HTTP文档高级处理模块,可以对HTML进行更细的处理。

3.1 使用HTMLParser模块

HTMLParser小巧、快速、使用简便,利用它可以分析HTML文档中的标签和数据等。HTMLParser采用了一种事件驱动的方式,是的模块在找到了一个特定的对象的时候,可以调用用户定义的函数来进行处理

处理函数都是以handle_开头的,是HTMLParser的成员函数。具体使用使,先继承HTMLParser类,然后重载这些函数即可。

HTMLParser中的handle_函数如下:

  • handle_startendtag 处理开始标签和结束标签,比如
  • handle_starttag 处理开始标签,比如
  • handle_endtag 处理结束标签,比如
  • handle_charref 处理特殊字符串,就是以&#开头的,一般是内码表示的字符
  • handle_entityref 处理一些特殊字符,以&开头的,比如  
  • handle_data 处理数据,就是data中间的那些数据
  • handle_comment 处理注释
  • handle_decl 处理

3.2 sgmllib的HTML文档处理

Deprecated since version 2.6: The sgmllib module has been removed in Python 3.

3.3 使用htmllib处理HTML文档

Deprecated since version 2.6: The htmllib module has been removed in Python 3.

你可能感兴趣的:(Python)