1 爬虫功能:
爬取某域名下所有网页,比如爬取python文档 https://docs.python.org/zh-cn/3/ ,爬取之后,获取离线文档
2 代码实现
开发环境: python3.6
import os
import sys
import http.client #2.7版本为httplib
import urllib.request
import formatter
import io
from html.parser import HTMLParser
class Retriever(object):
__slots__ = (\'url\', \'file\')
def __init__(self,url):
self.url, self.file = self.get_url_file(url)
def get_url_file(self, url, default = \'index.html\'):
#\'Create usable local filename from URL\'
parsed = urllib.request.urlparse(url)
host = parsed.netloc.split(\'@\')[-1].split(\':\')[0]
print(\'retriever host: %s\' %host)
filepath = \'%s%s\' %( host, parsed.path)
if not os.path.splitext(parsed.path)[1]:
filepath = os.path.join(filepath, default)
print(\'retriever filepath: %s\' % filepath)
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):
#\'Download URL to specific named file\'
try:
retval = urllib.request.urlretrieve(self.url,self.file)
except (IOError,http.client.InvalidURL) as e:
retval ( (\'***ERROR: bad URL "%s" %s\') %(self.url, e))
return retval
def parse_links(self):
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])
f = open(self.file, \'r\', encoding="UTF-8")
data = f.read()
f.close()
parser = AnchorParser()
parser.feed(data)
parser.close()
#output( (urllib.request.join(self.url, x) for x in parser.data ) )
if not hasattr(parser, \'data\'):
return []
return parser.data
class Crawler(object):
count = 0
def __init__(self, url):
self.q = [url]
self.seen = set()
parsed = urllib.request.urlparse(url)
host = parsed.netloc.split(\'@\')[-1].split(\':\')[0]
print("host: %s" %host)
#self.dom = \'.\'.join(host.split(\'.\')[-2:])
self.dom = host
print("dom: %s" %self.dom)
def get_page(self, url, media=False):
r = Retriever(url)
fname = r.download()[0]
print(\'donwload file name: %s\' %fname)
if fname[0] == \'*\':
print(fname + \'...skipping parse\')
return
Crawler.count += 1;
print("(%d,URL:%s, FILE:%s)" %(Crawler.count, url, fname))
self.seen.add(url)
ftype = os.path.splitext(fname)[1]
if ftype not in (\'.html\', \'htm\'):
return
print(r.parse_links())
for link in r.parse_links():
if link.startswith(\'mailto:\'):
print(\'...discarded, mailto link\')
continue
if not media:
ftype = os.path.splitext(link)[1]
if ftype in (\'.mp3\', \'.mp4\', \'.m4v\', \'.wav\'):
print( \'...discard, media file\')
continue
if ftype in (\'.epub\'):
print(\'...discard, epub file\')
continue
if not link.startswith(\'http://\') and not link.startswith(\'https://\'):
link = urllib.request.urljoin(url, link)
print(\'*\', link)
if link not in self.seen:
if self.dom not in link:
print(\'...discard,not in domain\')
else:
if link not in self.q:
self.q.append(link)
print(\'...new, added to Q\')
else:
print(\'...discard, already in Q\')
else:
print(\'...discarded, already processed\')
def go(self, media = False):
#\'Process next page in queue (if any) \'
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 = input(\'Entry starting URL: \')
except(KeyboardInterrupt, KeyError):
url = \'\'
if not url:
return
if not url.startswith(\'http://\') and \
not url.startswith(\'https://\'):
url = \'http://%s/\' %url
print(\'start url: %s\' %url )
robot = Crawler(url)
robot.go()
if __name__ == \'__main__\':
main()
3使用包包和函数
os.path
os.path.splitext
用法: os.path.splitext(“文件路径”) 分离文件名与扩展名;默认返回(fname,fextension)元组,可做分片操作
os.path.join
用法: 接两个或更多的路径名组件
1.如果各组件名首字母不包含’/’,则函数会自动加上
2.如果有一个组件是一个绝对路径,则在它之前的所有组件均会被舍弃
3.如果最后一个组件为空,则生成的路径以一个’/’分隔符结尾
os.path.dirname
语法:os.path.dirname(path)
功能:去掉文件名,返回目录
os.path.unlink
os.unlink(path) 方法用于删除文件,如果文件是一个目录则返回一个错误。
urllib.request
urllib.request.urlretrieve
python3中urllib.request模块提供的urlretrieve()函数。urlretrieve()方法直接将远程数据下载到本地。
urlretrieve(url, filename=None, reporthook=None, data=None)
参数url:下载链接地址
参数filename:指定了保存本地路径(如果参数未指定,urllib会生成一个临时文件保存数据。)
参数reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调,我们可以利用这个回调函数来显示当前的下载进度。
参数data:指post导服务器的数据,该方法返回一个包含两个元素的(filename, headers) 元组,filename 表示保存到本地的路径,header表示服务器的响应头
html.parse.HTMLParser
HTMLParser 类
- HTMLParser.feed(data):接收一个字符串类型的HTML内容,并进行解析
HTMLParser.
close
():当遇到文件结束标签后进行的处理方法。如果子类要复写该方法,需要首先调用HTMLParser累的close()HTMLParser.
reset
():重置HTMLParser实例,该方法会丢掉未处理的html内容HTMLParser.
getpos
():返回当前行和相应的偏移量HTMLParser.
handle_starttag
(tag, attrs):对开始标签的处理方法。例如,参数tag指的是div,attrs指的是一个(name,Value)的列表,参数tag指的是divHTMLParser.
handle_endtag
(tag):对结束标签的处理方法。例如HTMLParser.
handle_data
(data):对标签之间的数据的处理方法。test ,data指的是“test”HTMLParser.
handle_comment
(data):对HTML中注释的处理方
当然了,使用Python自带的HTMLParser还是比较麻烦的,需要手写处理Html标签的函数。