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):对开始标签的处理方法。例如<div id="main">,参数tag指的是div,attrs指的是一个(name,Value)的列表 -
HTMLParser.handle_endtag(tag):对结束标签的处理方法。例如</div>,参数tag指的是div -
HTMLParser.handle_data(data):对标签之间的数据的处理方法。<tag>test</tag>,data指的是“test” -
HTMLParser.handle_comment(data):对HTML中注释的处理方
当然了,使用Python自带的HTMLParser还是比较麻烦的,需要手写处理Html标签的函数。