# 本代码首先输入一个初始链接,然后读取下载之,在读取到的网页中找更多的链接,依次读取下载
# 环境python3.8
import os
import sys
import urllib
import urllib.request
from urllib.parse import urlparse
from http.client import InvalidURL
import re
# from html.parser import HTMLParser
# 创建下载器类
class Retriever:
# 表示实例只能拥有url和file两个属性
__slots__ = ("url", 'file')
def __init__(self, url):
self.url, self.file = self.get_file(url)
def get_file(self, url, default='index.html'):
"""
从url中得到可以使用的本地用户名
:param url:
:param default: 默认名设置为index.html
:return: url, 文件路径
"""
parsed = urlparse(url)
host = parsed.netloc.split('@')[-1].split(':')[0]
filepath = '%s%s' % (host, parsed.path)
# 如果url中没有文件扩展名,加入默认文件名称
if not os.path.splitext(parsed.path)[1]:
filepath = os.path.join(filepath, default)
# 获取链接的文件夹名称
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):
"""
下载文件
:return:本地文件名称
"""
try:
retval = urllib.request.urlretrieve(self.url, self.file)
except (IOError, InvalidURL) as e:
retval = (('***ERROR:bad URL "%s":%s' % (self.file, e)),)
return retval
def parse_links(self):
"""
解析获取下载到的文件中的url
:return:url 列表
"""
# 读取文件中的数据
try:
f = open(self.file, 'r')
data = f.read()
f.close()
list_url = re.findall("https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+", data)
# print(list_url)
# parser = HTMLParser()
# parser.feed(data)
# parser.close()
return list_url
except UnicodeDecodeError as e:
print(e)
return []
# 创建机器人类
class Crawler:
# 记录已经处理的url数
count = 0
def __init__(self, url):
# 在任务队列中的url
self.q = [url]
# 已经被使用过的url
self.seen = set()
# url拆分
parsed = urlparse(url)
# 获取主机名
host = parsed.netloc.split('@')[-1].split(':')[0]
# 获取域名
self.dom = '.'.join(host.split('.')[-2:])
def get_page(self, url, media=False):
"""
下载页面和解析到的链接,将下载到的页面中满足条件的url放入列表q
:param url: url地址
:param media: 默认为False,忽略媒体文件
:return:
"""
# 创建下载器对象
r = Retriever(url)
# 执行下载命令,并返回文件名称
fname = r.download()[0]
# 若下载过程中发生异常,则返回
if fname[0] == '*':
print(fname, '...skipping parse')
return
# 若下载完成,计数加1
Crawler.count += 1
# 打印文件信息
print('\n(', Crawler.count, ')')
print('URL:', url)
print('FILE', fname)
# 将当前url加入已处理的列表中
self.seen.add(url)
# 获取文件类型
ftype = os.path.splitext(fname)[1]
# 如果文件类型不属于htm、html则返回
if ftype not in ('.htm', '.html'):
return
# 遍历解析下载的页面得到的url
for link in r.parse_links():
# 如果是邮件地址,则跳过当前url
if link.startswith('mailto:'):
print('discarded, mailto link')
continue
# 媒体文件也要跳过
if not media:
if ftype in ('.mp3', '.mp4', '.m4v', '.wav'):
print('...discarded,media file')
continue
# url补全
if not link.startswith('http://') and not link.startswith('https://'):
link = urlparse(url, link)
print('*', link)
# 判断是否已经处理过该url
if link not in self.seen:
# 判断要被存储的链接是否属于当前域名,若不属于,则跳过
if self.dom not in link:
print(self.dom, link)
print('...discarded, not in domain')
else:
# 判断当前url是否已在任务队列中
if link not in self.q:
self.q.append(link)
print('...new, added to Q')
else:
print('..discarded, already in Q')
else:
print('...discarded, already processed')
def go(self, media=False):
"""
取q中的url地址进行处理,直至q为空
:param media:
:return:
"""
while self.q:
url = self.q.pop()
self.get_page(url, media)
def main():
# 如果在运行脚本时指定了一个,则使用该url,否则在交互模式下输入url
if len(sys.argv) > 1:
url = sys.argv[1]
else:
try:
url = input('Enter starting URL: ')
except (KeyboardInterrupt, EOFError):
url = ''
# 若url为空,则返回
if not url:
return
# 处理输入
if not url.startswith('http://') and not url.startswith('https://') and not url.startswith('ftp://'):
url = 'https://%s' % url
# 创建机器人对象
robot = Crawler(url)
# 启动机器人
robot.go()
if __name__ == "__main__":
main()
用python进行简单的页面抓取
最新推荐文章于 2024-05-02 10:14:31 发布