用python进行简单的页面抓取

# 本代码首先输入一个初始链接,然后读取下载之,在读取到的网页中找更多的链接,依次读取下载
# 环境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碎碎,python)