小言_互联网的博客

打开我的收藏夹 -- Python爬虫篇(1)

466人阅读  评论(0)


前言

这两天都没更新啥哈,太忙了。
今天这篇会比较特殊一些,因为爬虫我会的也写的差不多了,但是我还有一个背后隐藏能源还没拿出来用呢!!!
今天就打开这个隐藏能源,这可是一个大佬多年呕心沥血打造的,看看我能从中学到多少东西。


线程锁

哈哈,先来就讲这个和爬虫“不太沾边”的东西,是不是不好啊。
其实是很有关系的啦。如果你要靠自己大批量爬取内容,肯定是离不开多线程工作了嘛,要是单线程那速度你会很感动的。

但是多线程就有一个通病,资源竞态,这是跑不掉的。
那怎么解决这个问题,最直接的一个办法就是上锁。

all_urls = []       #url数组

g_lock = threading.Lock()  #初始化一个锁

while len(all_urls) > 0 :
    g_lock.acquire()  #在访问all_urls的时候,需要使用锁机制
    page_url = all_urls.pop()   #通过pop方法移除最后一个元素,并且返回该值
    g_lock.release() #使用完成之后及时把锁给释放,方便其他线程使用
 	······

retrying模块

当我们用 request 发起网络请求,时不时会遇到超时,当然不可能让这个请求一直阻塞,一般会设置一个超时时间,用 try except 抛出异常,避免程序中断。我们日常访问某网站时,有打不开的情况都会多刷新几次。因此,我们也需要让 python 进行重试。而 retrying 模块应运而生

为了表现 retrying 的重试功能,我们故意请求一个不规范的链接,如 www.baidu.com ,由于没有带 http 协议,request 会报错,从而触发 retrying 重试:

import requests
from retrying import retry

headers = {
   
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}

@retry(stop_max_attempt_number=3) # 表示重试以下代码三次
def _parse_url(url):
    print("-" * 30)
    response = requests.get(url, headers=headers, timeout=3)
    assert response.status_code == 200
    return response.content.decode()


def parse_url(url):
    try:
        html_str = _parse_url(url)
    except:
        html_str = None
    return html_str


if __name__ == "__main__":
    url = 'www.baidu.com'
    print(parse_url(url))

接着,你再把http加上去试试看。


aiohttp模块

在 Python 众多的 HTTP 客户端中,最有名的莫过于requests、aiohttp和httpx(说实话,今天之前,我只知道requests和那个urllib)。
在不借助其他第三方库的情况下,requests只能发送同步请求;aiohttp只能发送异步请求;httpx既能发送同步请求,又能发送异步请求。

同步和异步的概念:同步是串行,异步是并发。同步请求就是请求完了一个再请求一个,异步请求就是不管有没有结果回来,继续往下请求再说。

实在不知道要怎么说,来段代码吧:

import aiohttp
import asyncio

async def fetch_img_url(num):
    url = f'http://bbs.fengniao.com/forum/forum_101_{num}_lastpost.html'  # 字符串拼接
    # 或者直接写成 url = 'http://bbs.fengniao.com/forum/forum_101_1_lastpost.html'
    print(url)
    headers = {
   
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400',
    }

    async with aiohttp.ClientSession() as session:	# 创建一个会话,用于保持连接。保持连接的好处我们心照不宣哈
        # 获取轮播图地址
        async with session.get(url, headers=headers) as response:
            try:
                html = await response.text()  # 获取到网页源码	# await:等待网页数据返回
                print(html)

            except Exception as e:
                print("基本错误")
                print(e)

loop = asyncio.get_event_loop()	#创建线程
tasks = asyncio.ensure_future(fetch_img_url(1))
results = loop.run_until_complete(tasks)	#负责安排tasks中的任务
# task可以是单独的函数,也可以是列表

httpx我希望各位可以回去自行查找资料,我不急,我开班了再说这个。

这里带上一个大佬的评论:

tasks并不是只能开1024个协程,只是最多只能同时运行1024个协程(实际上会低于这个数),因为asycnio内部使用的是select ,而select的其中一个局限性就是最多只能有1024个fd(文件描述法)。官方不使用poll和epoll,我猜测是为了Windows的兼容性,因为后面两个虽然强大但不支持Windows。还有开多少个协程都只是充分利用CPU,你放心,它只是持续处于亢奋状态,应该不会吃不消。

受教了。


requests-html模块

requests-html和其他解析HTML库最大的不同点在于HTML解析库一般都是专用的,所以我们需要用另一个HTTP库先把网页下载下来,然后传给那些HTML解析库。而requests-html自带了这个功能,所以在爬取网页等方面非常方便。

不多废话,直接看它能干嘛,怎么干!!!

获取网页源码

from requests_html import HTMLSession

session = HTMLSession()
r = session.get('url')
// 查看页面内容
print(r.html.html)

获取链接

# 获取链接
print(r.html.links)	# 获取所有链接
print(r.html.absolute_links)	# 获取全部绝对链接

获取元素

这里只讲Xpath,这需要另一个函数xpath的支持,它有4个参数如下:

- selector,要用的XPATH选择器;
- clean,布尔值,如果为真会忽略HTML中style和script标签造成的影响(原文是sanitize,大概这么理解);
- first,布尔值,如果为真会返回第一个元素,否则会返回满足条件的元素列表;
- _encoding,编码格式。
print(r.html.xpath("//div[@id='menu']", first=True).text)
print(r.html.xpath("//div[@id='menu']/a"))
print(r.html.xpath("//div[@class='content']/span/text()"))

懂得都懂,不再赘述,不懂私聊。


JavaScript支持!!!

这个牛逼了,好多次被JavaScript拦在了门口。

有些网站是使用JavaScript渲染的,这样的网站爬取到的结果只有一堆JS代码,这样的网站requests-html也可以处理,关键一步就是在HTML结果上调用一下render函数,它会在用户目录(默认是~/.pyppeteer/)中下载一个chromium,然后用它来执行JS代码。下载过程只在第一次执行,以后就可以直接使用chromium来执行了。唯一缺点就是chromium下载实在太慢了。

>>> r = session.get('http://python-requests.org/')

>>> r.html.render()
[W:pyppeteer.chromium_downloader] start chromium download.
Download may take a few minutes.
[W:pyppeteer.chromium_downloader] chromium download done.
[W:pyppeteer.chromium_downloader] chromium extracted to: C:\Users\xxxx\.pyppeteer\local-chromium\571375
>>> r.html.search('Python 2 will retire in only {months} months!')['months']
'<time>25</time>'

render函数还有一些参数,顺便介绍一下(这些参数有的还有默认值,直接看源代码方法参数列表即可):

- retries: 加载页面失败的次数
- script: 页面上需要执行的JS脚本(可选)
- wait: 加载页面钱的等待时间(秒),防止超时(可选)
- scrolldown: 页面向下滚动的次数
- sleep: 在页面初次渲染之后的等待时间
- reload: 如果为假,那么页面不会从浏览器中加载,而是从内存中加载
- keep_page: 如果为真,允许你用r.html.page访问页面

比如说简书的用户页面上用户的文章列表就是一个异步加载的例子,初始只显示最近几篇文章,如果想爬取所有文章,就需要使用scrolldown配合sleep参数模拟下滑页面,促使JS代码加载所有文章。

下次再遇到这种情况就有据可循了。


先翻它四分钟之一,饿都饿死了,下篇再写吧。


转载:https://blog.csdn.net/qq_43762191/article/details/115292826
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场