整体文章目录
一、 当前章节目录
二、HTML介绍
2.1 HTML的历史
- HTML 1.0——在1993年6月作为互联网工程工作小组(IETF)工作草案发布(并非标准);
- HTML 2.0——1995年11月作为RFC 1866发布,在RFC 2854于2000年6月发布之后被宣布已经过时 ;
- HTML 3.2——1996年1月14日,W3C推荐标准 ; HTML 4.0——1997年12月18日,W3C推荐标准 ;
- HTML 4.01——1999年12月24日,是在HTML4.0基础上的微小改进,W3C推荐标准 ;
- HTML 5 的第一份正式草案已于2008年1月22日公布,仍继续完善。
2.2 SGML、HTML、XHTML、HTML5的关系
- SGML:
- 在 GML 的基础上进行整理,形成了一套非常严谨的文件描述方法。它的组成包括语法定义,DTD(W3C DTD 教程),文件实例三部分。
- SGML是国际上定义电子文档和内容描述的标准。
- SGML因太严谨规范达500多页,故而不易学、不易用、难以实现,所以在它的基础上又发展出了其他的更易用的
- HTML:是人们抽取了 SGML的一个微小子集而提取出来的。其早期规范比较松散,但比较易学。
- XHTML:它的出现是因为HTML扩充性不好,内容的表现跟不上时代的变化(如无法表示某些化学符号等),以及因为性能的问题,官方逐渐趋于严格的模式,所以使用 XML的严格规则的XHTML成了W3C 计划中 HTML 的替代者。
- HTML5:
- 经过一系列修订,到现在说的 HTML 一般指 HTML 4.01;
- 而现在的 HTML 5则是 HTML 的第五个修订版,其主要的目标是将互联网语义化,以便更好地被人类和机器阅读,并同时提供更好地支持各种媒体的嵌入。
- HTML5本身并非技术,而是标准。它所使用的技术早已很成熟,国内通常所说的html5实际上是html与css3及JavaScript和api等的一个组合,大概可以用以下公式说明:HTML5≈HTML+CSS3+JavaScript+API
2.3 HTML的标签
- 结构性标签
- 呈现性标签
- 超文本链接标签
- 框架页面标签
2.4 HTML的框架组成
<!DOCTYPE html>
<html>
<head>
<title>标题栏:第一个HTML页面</title>
</head>
<body>
<h2>第一个HTML页面</h2>
Hello,<b>World</b>
<!-- 这是注释 -->
<p>
可以从
<a href="http://www.w3c.org">W3C</a>
网站上找到HTML的语言规范
</p>
<br>©2019
</body>
</html>
三、URL的处理
3.1 统一资源定位符
一种包含有授权的URL的语法如下:
协议://用户名@密码:子域名.域名.顶级域名:端口号/目录/文件名.文件后缀?参数=值#标志
上面是一个比较完整的URL。一般URL都是在上面的基础上的一些简化,下面是一些URL示例。
- http://www.w3.org/2005/10/Process-20051014/activities.html
- ftp://ftp.test.com/pub/index.txt
- first.html
…/images/logo.jpg
3.2 URL的解析
In [1]: from urllib.parse import urlparse
In [2]: r = rlparse('http://alice:secret@www.hostname.com:80/%7Ealice/python.cgi?query=text#sample')
# 对URL进行解析
In [3]: r
Out[3]: ParseResult(scheme='http', netloc='alice:secret@www.hostname.com:80', path='/%7Ealice/python.cgi', params='', query='query=text', fragment='sample')
In [4]: r.scheme
Out[4]: 'http'
In [5]: r.netloc
Out[5]: 'alice:secret@www.hostname.com:80'
In [6]: r.path
Out[6]: '/%7Ealice/python.cgi'
In [7]: r.params
Out[7]: ''
In [8]: r.query
Out[8]: 'query=text'
In [9]: r.fragment
Out[9]: 'sample'
In [10]: r.username
Out[10]: 'alice'
In [11]: r.password
Out[11]: 'secret'
In [12]: r.hostname
Out[12]: 'www.hostname.com'
In [13]: r.port
Out[13]: 80
In [14]: r.geturl()
Out[14]: 'http://alice:secret@www.hostname.com:80/%7Ealice/python.cgi?query=text#sample'
In [15]: r2 = urlparse("www.python.org/about", "http")
# 使用了
In [16]: r2
Out[16]: ParseResult(scheme='http', netloc='', path='www.python.org/about', params='', query='', fragment='')
3.3 URL的拼合
In [1]: from urllib.parse import urljoin, urlsplit, urlunsplit
# 导入urljoin以及后面将使用的urlsplit、urlunsplit
In [2]: r = urljoin("http://www.zeroc.com", "ice.html")
# 将绝对URL和相对URL拼合
In [3]: r
Out[3]: 'http://www.zeroc.com/ice.html'
In [4]: r = urljoin("","")
In [5]: r
Out[5]: ''
In [6]: r = urljoin("http://www.python.org", "ftp://www.python.org/faq")
In [7]: r
Out[7]: 'ftp://www.python.org/faq'
In [8]: r = urljoin("http://www.python.org", "www.python.org/faq")
In [9]: r
Out[9]: 'http://www.python.org/www.python.org/faq'
In [10]: r = urljoin("http://www.python.org", "http://www.python.com/faq")
In [11]: r
Out[11]: 'http://www.python.com/faq'
3.4 URL的分解
In [1]: from urllib.parse import urljoin, urlsplit, urlunsplit
In [2]: r = urlsplit("http://python.org:80/faq.cgi?src=fie")
In [3]: r
Out[3]: SplitResult(scheme='http', netloc='python.org:80', path='/faq.cgi', query='src=fie', fragment='')
In [4]: r = urlunsplit(("http", "www.python.org", "faq", "", ""))
In [5]: r
Out[5]: 'http://www.python.org/faq'
In [6]: r = urlunsplit(urlsplit("http://www.python.org/faq?"))
In [7]: r
Out[7]: 'http://www.python.org/faq'
from urllib.parse import urlparse, urljoin, urlunsplit, urlsplit
abs_urls = ["http://www.python.org", "ftp://www.linux.org", "http://www.gtk.org", "file://"]
rel_url = "faq.html"
for abs_url in abs_urls:
url = urljoin(abs_url, rel_url) # 拼合URL
expected_url = url
scheme, netloc, path, query, fragment = urlsplit(url) # 分解URL
if scheme == "file":
print(url, "====>None")
continue
if scheme is not "ftp":
expected_url = urlunsplit(('http', netloc, path, query, fragment))
print(url, "====>", expected_url)
运行结果:
http://www.python.org/faq.html ====> http://www.python.org/faq.html
ftp://www.linux.org/faq.html ====> http://www.linux.org/faq.html
http://www.gtk.org/faq.html ====> http://www.gtk.org/faq.html
file:///faq.html ====>None
3.5 URL的编解码
In [1]: from urllib.parse import quote, quote_plus, unquote, unquote_plus
In [2]: r1 = quote("/~test/")
In [3]: r1
Out[3]: '/~test/'
In [4]: r2 = quote("/~test/public html")
In [5]: r2
Out[5]: '/~test/public%20html'
# 空格被替换成%20
In [6]: r3 = quote_plus("/~test/public html")
In [7]: r3
Out[7]: '%2F~test%2Fpublic+html'
In [8]: unquote_plus(r3)
Out[8]: '/~test/public html'
In [9]: unquote(r2)
Out[9]: '/~test/public html'
In [10]: r = quote("/~test/", "~/")
In [11]: r
Out[11]: '/~test/'
3.6 中文的编解码
In [1]: from urllib.parse import quote, unquote
In [2]: quote("URL编码")
Out[2]: 'URL%E7%BC%96%E7%A0%81'
# 对URL进行编码
In [3]: r = quote("URL编码")
In [4]: unquote(r)
Out[4]: 'URL编码'
# 对URL进行解码
In [5]: print(unquote(r))
Out[5]: URL编码
In [6]: unquote("%E8%BF%99%E6%98%AF%E4%B8%80%E6%AE%B5%E7%A7%98%E5%AF%86%E7%9A%84%E6%B6%88%E6%81%AF")
Out[6]: '这是一段秘密的消息'
3.7 查询参数的编码
In [1]: from urllib.parse import urlencode
import urllib
import urllib.parse
In [2]: urlencode([('keyword1', 'value1'),('keyword2', 'value2')])
Out[2]: 'keyword1=value1&keyword2=value2'
# urlencode方法的作用就是将调查的参数值对返回成URL编码的形式
In [3]: urlencode({'keyword2':'value2','keyword1':'value1'})
Out[3]: 'keyword2=value2&keyword1=value1'
In [4]: a = {'keyword1':'value1', 'keyword2':'value2'}
In [5]: b = {'keyword2':'value2', 'keyword1':'value1'}
In [6]: a is b
Out[6]: False
In [7]: a == b
Out[7]: True
In [8]: urllib.parse.urlencode([('keyword',('value1', 'value2', 'value3'))])
Out[8]: 'keyword=%28%27value1%27%2C+%27value2%27%2C+%27value3%27%29'
# 默认为False,整个进行编码
In [9]: urllib.parse.urlencode([('keyword',('value1', 'value2', 'value3'))], True)
Out[9]: 'keyword=value1&keyword=value2&keyword=value3'
# 为True时,每个值都和keyword组成一个查询参数值对
In [10]: r = urlencode([('keyword', ('value1', 'value2', 'value3'))])
In [11]: urllib.parse.unquote_plus(r)
Out[11]: "keyword=('value1', 'value2', 'value3')"
四、CGI的使用
4.1 CGI介绍
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
print("Content-Type: text/html")
print()
print("Content-Type: text/html\n\n")
print('<!DOCTYPE html ">')
print('<html>')
print('<head>')
# 省略部分代码
print('<title>标题栏:第一个HTML页面</title>')
print('</head>')
print('<body>')
print('<h2>第一个HTML页面</h2>')
print('Hello,<b>World</b>')
print('<!-- 这是注释 -->')
print('<p>')
print('可以从')
print('<a href="http://www.w3c.org">W3C</a>')
print('网站上找到HTML的语言规范')
print('</p>')
print('<br>©2019')
print('</body>')
print('</html>')
运行结果:
4.2 获取CGI环境信息
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
print("Content-Type: text/plain\n\n")
import datetime
print(datetime.datetime.now())
运行结果:
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
import os
print("Content-Type: text/html\n\n")
remote_addr = os.environ['REMOTE_ADDR']
if remote_addr == '127.0.0.1':
print("来自本地的访问")
else:
print("来自外部的访问")
运行结果:
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
print("Content-Type: text/html\n\n")
import cgi
cgi.print_environ()
运行结果:
4.3 解析用户的输入
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
print("Content-Type: text/html\n\n")
import cgi
form = cgi.FieldStorage()
for key in form.keys():
print(key, "==>", form[key].value)
print("<br/>")
运行结果:
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
print("Content-Type: text/html\n\n")
import cgi
form = cgi.FieldStorage()
for key in form.keys():
print(key, "==>", form.getlist(key))
print("<br/>")
运行结果:
#!C:\Users\86155\AppData\Local\Programs\Python\Python39\python.exe
# 指定执行cgi脚本文件的程序
# -*- coding: utf-8 -*-
print("Content-Type: text/html\n\n")
import cgi
form = cgi.FieldStorage()
for key in form.keys():
for value in form.getlist(key):
print(key, "==>", cgi.escape(value)) # 使用escape()进行转义
print("<br/>")
运行结果(需要显示出特殊字符,但是未调试出来,不知道原因):
五、获取HTML资源
5.1 使用urlopen和urlretrieve获取HTTP资源
In [1]: from urllib.request import urlopen
In [2]: r = urlopen("http://www.python.org")
In [3]: print(r.read()) # 输出读取的内容
Out[3]: # 见下图
from urllib.request import urlopen
fp = urlopen("http://www.python.org")
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)
运行结果:
retrieved 50126 bytes from https://www.python.org/
In [1]: from urllib.request import urlretrieve
In [2]: filename, m = urlretrieve("http://www.baidu.com")
In [3]: filename
Out[3]: 'C:\\Users\\86155\\AppData\\Local\\Temp\\tmpq4vco5dl'
In [4]: filename, m = urlretrieve("http://www.baidu.com", filename="D:/baidu.html")
In [5]: filename
Out[5]: 'D:/baidu.html'
In [1]: from urllib.request import urlretrieve
In [2]: def reporthook(block_count, block_size, file_size):
if file_size == -1:
print("retrieved data", block_count*block_size)
else:
print("retrieve data", block_count*block_size, "/", file_size)
In [3]: urlretrieve("http://www.python.org", filename="", reporthook = reporthook)
retrieve data 0 / 50807
retrieve data 8192 / 50807
retrieve data 16384 / 50807
retrieve data 24576 / 50807
retrieve data 32768 / 50807
retrieve data 40960 / 50807
retrieve data 49152 / 50807
retrieve data 57344 / 50807
Out[3]: ('C:\\Users\\86155\\AppData\\Local\\Temp\\tmpkduc697l',
<http.client.HTTPMessage at 0x1e5f42067f0>)
In [4]: urlretrieve("http://www.google.cn", filename="", reporthook = reporthook)
retrieve data 0 / 1436
retrieve data 8192 / 1436
Out[4]: ('C:\\Users\\86155\\AppData\\Local\\Temp\\tmpiknw4w18',
<http.client.HTTPMessage at 0x1e5f4206d90>)
from urllib.request import urlretrieve
def download(url, filename=""):
def reporthook(block_count, block_size, file_size):
if file_size == -1:
print("Can'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
if __name__ == '__main__':
url = "https://www.baidu.com"
print(download(url))
运行结果:
5.2 分析返回资源的相关信息
In [1]: from urllib.request import urlopen
In [2]: r = urlopen("http://baidu.com")
In [3]: r.geturl()
Out[3]: 'http://baidu.com'
In [4]: r.url
Out[4]: 'http://baidu.com'
In [1]: from urllib.request import urlopen
In [2]: r = urlopen("http://www.baidu.com")
In [3]: m = r.info()
In [4]: m.get_content_type()
Out[4]: 'text/html'
In [5]: m.get_content_maintype()
Out[5]: 'text'
In [6]: m.get_content_subtype()
Out[6]: 'html'
In [7]: m
Out[7]: <http.client.HTTPMessage at 0x145ab8b5e80>
In [8]: for k, v in r.info().items():
print (k, "=", v)
Bdpagetype = 1
Bdqid = 0x903d8eb8000225e3
Cache-Control = private
Content-Type = text/html;charset=utf-8
Date = Mon, 24 May 2021 01:08:59 GMT
Expires = Mon, 24 May 2021 01:08:31 GMT
P3p = CP=" OTI DSP COR IVA OUR IND COM "
P3p = CP=" OTI DSP COR IVA OUR IND COM "
Server = BWS/1.1
Set-Cookie = BAIDUID=7047AED9366C7DE11ED8072D2B784B65:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie = BIDUPSID=7047AED9366C7DE11ED8072D2B784B65; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie = PSTM=1621818539; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie = BAIDUID=7047AED9366C7DE190B9442796DA96A3:FG=1; max-age=31536000; expires=Tue, 24-May-22 01:08:59 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Set-Cookie = BDSVRTM=0; path=/
Set-Cookie = BD_HOME=1; path=/
Set-Cookie = H_PS_PSSID=; path=/; domain=.baidu.com
Traceid = 1621818539028838759410393620435966240227
Vary = Accept-Encoding
Vary = Accept-Encoding
X-Ua-Compatible = IE=Edge,chrome=1
Connection = close
Transfer-Encoding = chunked
In [9]: r.headers
Out[9]: <http.client.HTTPMessage at 0x145ab8b5e80>
5.3 使用http.client模块获取资源
In [1]: import http.client # 导入http.client模块
# 初始化三个https链接
In [2]: conn1 = http.client.HTTPSConnection("www.python.org")
In [3]: conn1.request("GET", "/doc/") # 指定request请求的方法和请求的链接地址
In [4]: r1 = conn1.getresponse()
In [5]: print(r1.status, r1.reason)
Out[5]: 200 OK
In [6]: r1.read(256)
Out[6]: b'<!doctype html>\n<!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 8]> <html class="no-js ie8 lt-ie9"> <![endif]-'
In [7]: conn1.close()
In [8]: conn1.request("GET", "/test.html") # 请求一个不存在的文件或地址
In [9]: r2 = conn1.getresponse()
In [10]: print(r2.status, r2.reason)
Out[10]: 404 Not Found
In [11]: conn1.close()
In [12]: conn1.putrequest("GET", "/doc/")
In [13]: conn1.putheader("User-Agent", "Mozilla/4.0(compatible;MSIE 6.0;Windows NT 5.1)")
In [14]: conn1.putheader("Accept", "*/*")
In [15]: conn1.endheaders()
In [16]: r3 = conn1.getresponse()
200 OK
In [17]: print(r3.status, r3.reason)
In [18]: data3 = r3.read(256).decode()
In [19]: data3
Out[19]: '\n<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta http-equiv="content-type" content="text/html; charset=utf-8">\n <meta name="robots" content="NONE,NOARCHIVE">\n <title>403 Forbidden</title>\n <style type="text/css">\n html * { padding:0; margin:0; }\n '
六、HTML文档的解析
from urllib.parse import urlparse, urljoin
from urllib.request import urlopen
from html import parser
class CheckHTML(parser.HTMLParser): # 从HTMLParser类中继承
available = True
def handle_data(self, data): # 定义处理数据的方法
if "404 Not Found" in str(data) or "Error 404" in str(data): # 当含有特定字符串的时候
self.available = False
check_urls = ["/about/", "/blogs/", "/downloads/", "/faq/"] # 需要检查的URL
for url in check_urls:
new_url = urljoin("http://www.python.org/", url) # 拼合URL
try:
fp = urlopen(new_url) # 打开URL资源
data = fp.read() # 读取URL资源
fp.close()
p = CheckHTML() # 生成一个CheckHTML类对象实例
p.feed(str(data)) # 解析上面获得的数据
p.close()
# 判断URL是否存在
if p.available:
print(new_url, "==> OK")
except:
print(new_url, "==> Not Found")
运行结果:
http://www.python.org/about/ ==> OK
http://www.python.org/blogs/ ==> OK
http://www.python.org/downloads/ ==> OK
http://www.python.org/faq/ ==> Not Found
七、习题
习题:
- 什么是http协议?简单叙述http协议的连接过程。
- 解析https://www.python.org/,使用urllib抓取网页的内容,并打印出来。
- 根据示例代码练习相关库的使用。
答案:
- HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写。HTTP 协议和 TCP/IP 协议族内的其他众多的协议相同, 用于客户端和服务器之间的通信。请求访问文本或图像等资源的一端称为客户端, 而提供资源响应的一端称为服务器端。
连接过程:如果从编程步骤上来讲,也就是套接字编程里面的步骤来说,需要先通过域名获得服务器的IP地址,调用相关的API可以完成这一步,然后需要将文本表示的IP地址转换为套接字编程要求的格式,然后端口号80也要转换成相应的格式,一般是一个结构体,然后建立套接字,调用connect函数就能建立连接了。
2.代码如下。
from urllib.request import urlopen
fp = urlopen("http://www.python.org")
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)