【问题标题】:How make simple fast request with "requests" module python?如何使用“请求”模块python进行简单的快速请求?
【发布时间】:2019-06-07 01:12:12
【问题描述】:

我是python的初学者,我只是想用requestsBeautifulSoup这个Website模块来抓取网页。

还有我的简单代码:

import requests, time, re, json
from bs4 import BeautifulSoup as BS

url = "https://www.jobstreet.co.id/en/job-search/job-vacancy.php?ojs=6"

def list_jobs():
    try:
        with requests.session() as s:
            st = time.time()
            s.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'}
            req = s.get(url)
            soup = BS(req.text,'html.parser')
            attr = soup.findAll('div',class_='position-title header-text')
            pttr = r".?(.*)Rank=\d+"
            lists = {"status":200,"result":[]}
            for a in attr:
                sr = re.search(pttr, a.find("a")["href"])
                if sr:
                    title = a.find('a')['title'].replace("Lihat detil lowongan -","").replace("\r","").replace("\n","")
                    url = a.find('a')['href']
                    lists["result"].append({
                        "title":title,
                        "url":url,
                        "detail":detail_jobs(url)
                    })
            print(json.dumps(lists, indent=4))
            end = time.time() - st
            print(f"\n{end} second")
    except:
        pass

def detail_jobs(find_url):
    try:
        with requests.session() as s:
            s.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'}
            req = s.get(find_url)
            soup = BS(req.text,'html.parser')
            position = soup.find('h1',class_='job-position').text
            name = soup.find('div',class_='company_name').text.strip("\t")
            try:
                addrs = soup.find('div',class_='map-col-wraper').find('p',{'id':'address'}).text
            except Exception:
                addrs = "Unknown"
            try:
                loct = soup.find('span',{'id':'single_work_location'}).text
            except Exception:
                loct = soup.find('span',{'id':'multiple_work_location_list'}).find('span',{'class':'show'}).text        
            dests = soup.findAll('div',attrs={'id':'job_description'})
            for select in dests:
                txt = select.text if not select.text.startswith("\n") or not select.text.endswith("\n") else select.text.replace("\n","")
                result = {
                    "name":name,
                    "location":loct,
                    "position":position,
                    "description":txt,
                    "address":addrs
                }
                return result
    except:
        pass

它们都运行良好,但需要很长时间才能显示结果时间总是在 13/17 秒以上

我不知道如何提高我的请求速度

我尝试在堆栈和谷歌上搜索,他们说使用 asyncio 但对我来说很难。

如果有人有简单的技巧如何通过简单的操作来提高速度,我很感激..

对不起,我的英语不好

【问题讨论】:

    标签: python performance beautifulsoup python-requests


    【解决方案1】:

    通过网页抓取等项目学习 Python 非常棒。这就是我被介绍给 Python 的方式。也就是说,要提高抓取速度,您可以做三件事:

    1. 将 html 解析器更改为更快的值。 'html.parser' 是其中最慢的。尝试更改为“lxml”或“html5lib”。 (阅读https://www.crummy.com/software/BeautifulSoup/bs4/doc/

    1. 删除循环和正则表达式,因为它们会减慢您的脚本。只需使用 BeautifulSoup 工具、文本和条带,并找到正确的标签。(请参阅下面的脚本)

    2. 由于网页抓取的瓶颈通常是 IO,等待从网页获取数据,使用异步或多线程将提高速度。在下面的脚本中,我使用了多线程。目的是同时从多个页面中提取数据。

    因此,如果我们知道最大页面数,我们可以将我们的请求分块到不同的范围并分批拉取它们:)

    代码示例:

    from collections import defaultdict
    from concurrent.futures import ThreadPoolExecutor
    from datetime import datetime
    
    import requests
    from bs4 import BeautifulSoup as bs
    
    data = defaultdict(list)
    
    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'}
    
    def get_data(data, headers, page=1):
    
        # Get start time
        start_time = datetime.now()
        url = f'https://www.jobstreet.co.id/en/job-search/job-vacancy/{page}/?src=20&srcr=2000&ojs=6'
        r = requests.get(url, headers=headers)
    
        # If the requests is fine, proceed
        if r.ok:
            jobs = bs(r.content,'lxml').find('div',{'id':'job_listing_panel'})
            data['title'].extend([i.text.strip() for i in jobs.find_all('div',{'class':'position-title header-text'})])
            data['company'].extend([i.text.strip() for i in jobs.find_all('h3',{'class':'company-name'})])
            data['location'].extend([i['title'] for i in jobs.find_all('li',{'class':'job-location'})] )
            data['desc'].extend([i.text.strip() for i in jobs.find_all('ul',{'class':'list-unstyled hidden-xs '})])
        else:
            print('connection issues')
        print(f'Page: {page} | Time taken {datetime.now()-start_time}')
        return data
        
    
    def multi_get_data(data,headers,start_page=1,end_page=20,workers=20):
        start_time = datetime.now()
        # Execute our get_data in multiple threads each having a different page number
        with ThreadPoolExecutor(max_workers=workers) as executor:
            [executor.submit(get_data, data=data,headers=headers,page=i) for i in range(start_page,end_page+1)]
        
        print(f'Page {start_page}-{end_page} | Time take {datetime.now() -     start_time}')
        return data
    
    
    # Test page 10-15
    k = multi_get_data(data,headers,start_page=10,end_page=15)
    

    结果:

    解释multi_get_data函数:

    此函数将在不同线程中调用 get_data 函数并传递所需的参数。目前,每个线程都有一个不同的页码来调用。最大工作线程数设置为 20,即 20 个线程。您可以相应地增加或减少。

    我们创建了可变数据,一个默认字典,它包含列表。所有线程都将填充这些数据。然后可以将此变量转换为 json 或 Pandas DataFrame :)

    如您所见,我们有 5 个请求,每个请求用时不到 2 秒,但总数仍不到 2 秒;)

    享受网页抓取。

    更新_:22/12/2019

    我们还可以通过使用带有单个标头更新的会话来获得一些速度。所以我们不必在每次通话时都开始会话。

    from requests import Session
    
    s = Session()
    headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) '\
                             'AppleWebKit/537.36 (KHTML, like Gecko) '\
                             'Chrome/75.0.3770.80 Safari/537.36'}
    # Add headers
    s.headers.update(headers)
    
    # we can use s as we do requests
    # s.get(...)
    ...
    

    【讨论】:

    • 优秀的佩森!
    • 它太棒了,我会尝试并改进它我想知道如何提出一个简单的请求但速度更快,也许我可以使用你的棘手;)
    • 播放并调整它以满足您的需求。它在您的示例中收集了相同的数据。我放弃了数据的打印,因为它会减慢这个过程。您可以在之后打印您的数据,例如将 pandas 导入为 pd 并将 k 添加为 df = pd.DataFrame(k)。现在您可以打印 print(df.head()) 并将数据加载到 csv 为 df.to_csv('data.csv', index=False,sep=';')
    【解决方案2】:

    尝试使用scrapy,它将为您处理网站通信(请求/响应)。

    如果您提出许多请求,您将被阻止,他们正在使用Cloudflare 产品

    【讨论】:

    • 添加一个关于如何使用scrapy的简单示例会很好;)
    【解决方案3】:

    这是我的建议,即编写具有良好架构的代码并将其划分为函数并编写更少的代码。这是使用请求的示例之一:

    from requests import get
    from requests.exceptions import RequestException
    from contextlib import closing
    from bs4 import BeautifulSoup
    
    def simple_get(url):
        """
        Attempts to get the content at `url` by making an HTTP GET request.
        If the content-type of response is some kind of HTML/XML, return the
        text content, otherwise return None.
        """
        try:
            with closing(get(url, stream=True)) as resp:
                if is_good_response(resp):
                    return resp.content
                else:
                    return None
    
        except RequestException as e:
            log_error('Error during requests to {0} : {1}'.format(url, str(e)))
            return None
    
    
    def is_good_response(resp):
        """
        Returns True if the response seems to be HTML, False otherwise.
        """
        content_type = resp.headers['Content-Type'].lower()
        return (resp.status_code == 200 
                and content_type is not None 
                and content_type.find('html') > -1)
    
    
    def log_error(e):
        """
        It is always a good idea to log errors. 
        This function just prints them, but you can
        make it do anything.
        """
        print(e)
    

    在需要时间的地方调试您的代码并找出它们并在此处讨论。这样可以帮助您解决问题。

    【讨论】:

      【解决方案4】:

      瓶颈是服务器对简单请求的响应缓慢。

      尝试并行请求。

      您也可以使用线程来代替 asyncio。以下是 Python 中并行任务的前一个问题解释:

      Executing tasks in parallel in python

      请注意,如果您在未经许可的情况下进行抓取,智能配置的服务器仍会减慢您的请求或禁止您。

      【讨论】:

        猜你喜欢
        • 2012-11-01
        • 1970-01-01
        • 2019-02-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多