优雅地处理不同响应码的异步请求

urls.txt 文件

https://regex101.com/
https://docs.python.org/3/this-url-will-404.html
https://www.nytimes.com/guides/
https://www.mediamatters.org/
https://1.1.1.1/
https://www.politico.com/tipsheets/morning-money
https://www.bloomberg.com/markets/economics
https://www.ietf.org/rfc/rfc2616.txt
import sys
import aiohttp
import asyncio
import logging
from typing import IO
from aiohttp import ClientSession

logging.basicConfig(
    format="%(asctime)s %(levelname)s:%(name)s: %(message)s",
    level=logging.DEBUG,  # 设置日志级别
    stream=sys.stderr,
)
logger = logging.getLogger("areq")
logging.getLogger("chardet.charsetprober").disabled = True


async def fetch_html(url: str, session: ClientSession, **kwargs) -> str:  # 获取源码
    resp = await session.request(method="GET", url=url, **kwargs)
    resp.raise_for_status()  # 抛出状态类型异常
    logger.info("响应码:[%s] URL: %s", resp.status, url)
    html = await resp.text()
    return html


async def parse(url: str, session: ClientSession, **kwargs) -> set:  # 解析链接
    from urllib.parse import urljoin
    from urllib.error import URLError
    from aiohttp import ClientError
    from aiohttp.http_exceptions import HttpProcessingError
    found = set()
    try:
        html = await fetch_html(url=url, session=session, **kwargs)
    except (ClientError, HttpProcessingError) as e:
        logger.error(
            "aiohttp异常 %s [%s]: %s", url,
            getattr(e, "status", None), getattr(e, "message", None))
        return found
    except Exception as e:
        logger.exception("发生非aiohttp异常:  %s", getattr(e, "__dict__", {}))
        return found
    else:
        import re
        HREF_RE = re.compile(r'href="(.*?)"')
        # 扫描当前页面的全部链接
        for link in HREF_RE.findall(html):
            try:
                # 拼接链接,适合扫描全站
                abslink = urljoin(url, link)
            except (URLError, ValueError):
                logger.exception("解析错误的链接: %s", link)
                pass
            else:
                # 添加链接
                found.add(abslink)
        logger.info(f"在 {url}发现 {len(found)}条链接")
        # 当前页面的全部链接
        return found


async def write_one(file: IO, url: str, **kwargs) -> None:
    import aiofiles
    res = await parse(url=url, **kwargs)
    if not res:
        return None  # 没有响应数据
    async with aiofiles.open(file, "a") as f:
        for p in res:
            await f.write(f"{url}\t{p}\n")
        logger.info("根据资源链接写入结果: %s", url)


async def main(file: IO, urls: set, **kwargs)->None:
    async with ClientSession() as session:
        tasks = [write_one(file, url, session=session, **kwargs)
                 for url in urls]
        await asyncio.gather(*tasks)


if __name__ == '__main__':
    import pathlib
    # 判断版本信息大于3.7版本
    assert sys.version_info >= (3, 7), "Script requires Python 3.7+."
    # 获取当前文件目录路径
    here = pathlib.Path(__file__).parent
    # joinpath:合并完整的文件夹路径
    with open(here.joinpath("urls.txt")) as infile:
        # 读取全部数据,形成一个字典
        urls = set(map(str.strip, infile))
    outpath = here.joinpath("foundurls.txt")
    with open(outpath, "w") as outfile:
        outfile.write("source_url\tparsed_url\n")
    # 运行协程
    asyncio.run(main(file=outpath, urls=urls))

输出:

2019-12-16 20:41:28,711 DEBUG:asyncio: Using selector: SelectSelector
2019-12-16 20:41:29,662 INFO:areq: 响应码:[200] URL: https://www.ietf.org/rfc/rfc2616.txt
2019-12-16 20:41:29,746 INFO:areq: 响应码:[200] URL: https://1.1.1.1/
2019-12-16 20:41:30,153 INFO:areq: 在 https://1.1.1.1/发现 13条链接
2019-12-16 20:41:30,164 INFO:areq: 在 https://www.ietf.org/rfc/rfc2616.txt发现 0条链接
2019-12-16 20:41:30,172 INFO:areq: 根据资源链接写入结果: https://1.1.1.1/
2019-12-16 20:41:30,342 INFO:areq: 响应码:[200] URL: https://www.mediamatters.org/
2019-12-16 20:41:30,786 INFO:areq: 响应码:[200] URL: https://regex101.com/
2019-12-16 20:41:30,850 INFO:areq: 在 https://www.mediamatters.org/发现 116条链接
2019-12-16 20:41:30,871 INFO:areq: 根据资源链接写入结果: https://www.mediamatters.org/
2019-12-16 20:41:31,292 INFO:areq: 响应码:[200] URL: https://www.politico.com/tipsheets/morning-money
2019-12-16 20:41:31,367 INFO:areq: 在 https://www.politico.com/tipsheets/morning-money发现 149条链接
2019-12-16 20:41:31,400 INFO:areq: 根据资源链接写入结果: https://www.politico.com/tipsheets/morning-money
2019-12-16 20:41:33,031 INFO:areq: 在 https://regex101.com/发现 24条链接
2019-12-16 20:41:33,036 INFO:areq: 根据资源链接写入结果: https://regex101.com/
2019-12-16 20:41:36,827 ERROR:areq: aiohttp异常 https://docs.python.org/3/this-url-will-404.html [404]: Not Found
2019-12-16 20:41:50,115 ERROR:areq: aiohttp异常 https://www.nytimes.com/guides/ [None]: None
2019-12-16 20:41:50,116 ERROR:areq: aiohttp异常 https://www.bloomberg.com/markets/economics [None]: None
[Finished in 23.9s]
【为什么学爬虫?】        1、爬虫入手容易,但是深入较难,如何写出高效率的爬虫,如何写出灵活性高可扩展的爬虫都是一项技术活。另外在爬虫过程中,经常容易遇到被反爬虫,比如字体反爬、IP识别、验证等,如何层层攻克难点拿到想要的数据,这门课程,你都能学到!        2、如果是作为一个其他行业的开发者,比如app开发,web开发,学习爬虫能让你加强对技术的认知,能够开发出更加安全的软件和网站 【课程设计】 一个完整的爬虫程序,无论大小,总体来说可以分成三个步骤,分别是:网络请求:模拟浏览器的行为从网上抓取数据。数据解析:将请求下来的数据进行过滤,提取我们想要的数据。数据存储:将提取到的数据存储到硬盘或者内存中。比如用mysql数据库或者redis等。那么本课程也是按照这几个步骤循序渐进的进行讲解,带领学生完整的掌握每个步骤的技术。另外,因为爬虫的多样性,在爬取的过程中可能会发生被反爬、效率低下等。因此我们又增加了两个章节用来提高爬虫程序的灵活性,分别是:爬虫进阶:包括IP代理,多线程爬虫,图形验证识别、JS加密解密、动态网页爬虫、字体反爬识别等。Scrapy和分布式爬虫:Scrapy框架、Scrapy-redis组件、分布式爬虫等。通过爬虫进阶的知识点我们能应付大量的反爬网站,而Scrapy框架作为一个专业的爬虫框架,使用他可以快速提高我们编写爬虫程序的效率和速度。另外如果一台机器不能满足你的需求,我们可以用分布式爬虫让多台机器帮助你快速爬取数据。 从基础爬虫到商业化应用爬虫,本套课程满足您的所有需求!【课程服务】 专属付费社群+定期答疑
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值