【问题标题】:Having trouble using decorator within a python scraper在 python 刮板中使用装饰器时遇到问题
【发布时间】:2019-05-02 14:17:54
【问题描述】:

我在 python 中编写了一个脚本,通过跟踪其登录页面的基本链接来获取一些帖子的链接。如果我坚持传统的方法,我自己也可以刮。

但是,我的目标是使用 decorator 来做同样的事情。看来我已经接近了,但是在将链接从函数get_links() 传递到get_target_link() 时我被卡住了。我使用return func() 作为函数get_target_link() 中的占位符,因为我不知道如何传递链接。函数get_links() 中有打印语句(如果未注释则有效),以确保我在正确的轨道上。

如何将链接 return linklistget_links() 传递到 get_target_link(),以便在必要时重新使用它们?

这是我迄今为止尝试过的:

import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

url = "https://www.janglo.net/component/option,com_sobi2/"

def get_links(func):
    linklist = []
    res = requests.get(func())
    soup = BeautifulSoup(res.text,"lxml")
    for item in soup.select(".sobi2ItemTitle a"):
        linklist.append(urljoin(url,item.get("href")))
    #print(linklist)
    return linklist

    def get_target_link():
        return func()  #All I need to do is fix this line
    return get_target_link

@get_links
def get_info():
    res = requests.get(url)
    soup = BeautifulSoup(res.text,"lxml")
    for items in soup.select("#sobi2CatListSymbols .sobi2SubcatsListItems a[title]"):
        if items.text=="Tutors":
            ilink = f"{urljoin(url,items.get('href'))}"
    return ilink

if __name__ == '__main__':
    for links in get_info():
        print(links)

Post Script: I only would like to comply with the logic I've tried to apply above.

@sir Andersson (Can you explain how you want to re-use them if necessary) 的更新:

def get_target_link():
    titles = []
    new_links =  func()
    for new_link in new_links:
        res = requests.get(new_link)
        soup = BeautifulSoup(res.text)
        titles.append(soup.select_one("h1").text)
    return titles
return get_target_link

我想创建装饰函数,使其类似于@Carlos Mermingas:

import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

url = "https://www.janglo.net/component/option,com_sobi2/"

def get_info(link):
    res = requests.get(url)
    soup = BeautifulSoup(res.text,"lxml")
    for items in soup.select("#sobi2CatListSymbols .sobi2SubcatsListItems a[title]"):
        if items.text=="Tutors":
            ilink = f"{urljoin(url,items.get('href'))}"
    return ilink

def get_links(tlink):
    linklist = []
    res = requests.get(tlink)
    soup = BeautifulSoup(res.text,"lxml")
    for item in soup.select(".sobi2ItemTitle a"):
        linklist.append(urljoin(url,item.get("href")))
    return linklist

def get_target_link(link):
    titles = []
    res = requests.get(link)
    soup = BeautifulSoup(res.text,"lxml")
    titles.append(soup.select_one("h1").text)
    return titles

if __name__ == '__main__':
    item = get_info(url)
    for nlink in get_links(item):
        for ititle in get_target_link(nlink):
            print(ititle)

【问题讨论】:

  • 您能否解释一下您希望如何在必要时重复使用它们?您想重用装饰器中定义的变量吗?为什么不只创建返回此链接列表的单独函数?
  • 为什么你必须或想要使用装饰器?也许如果您发布对您有用的“常规方法”,将会阐明您的意图。此外,我发现您当前的 get_links 实现存在一些问题:1) 它两次调用装饰函数 (func()) 和 2) 它返回一个列表而不是可调用函数 (return linklist)。
  • 查看更新@sir Andersson。
  • 问题已编辑以表明我的意图@Carlos Mermingas。
  • 感谢您发布原始代码。我仍然不清楚装饰器如何解决这里的问题。

标签: python python-3.x function web-scraping decorator


【解决方案1】:

有点长的帖子,老实说,我已经停止阅读你的第一个 python 错误。

让我为你修好它,然后你告诉我这是否是缺少的。这就是装饰器模式在 python 中的工作原理。

一开始有点奇怪,有点像船运船,但很聪明。

装饰器是返回要调用的函数而不是另一个函数的函数。

让我们想象一下这个功能,没有装饰。

>>> def some_func(number):
...      return f'Number is {number}'
...
>>> print(some_func(10))
Number is 10

为了装饰这个功能,假设我们正在添加模糊测试;在这里和那里增加一些延迟是很常见的。

>>> def fuzz():
...     def fuzz_decorator(func):
...         def fuzz_wrapper(*args, **kwargs):
...             print('fuzz') # this is our added functionality
...             return func(*args, **kwargs) # call whatever we're decorating
...         return fuzz_wrapper
...     return fuzz_decorator
...
>>> @fuzz()
... def some_func(number):
...     return f'Number is {number}'
...
>>> print(some_func(10))
fuzz
Number is 10

fuzz() 是一个函数,它返回一个接受函数fuzz_decorator(func) 的函数,并返回一个新函数,该函数向func 添加一些功能,同时在某些时候调用func 本身。

希望这不会令人困惑。但你错了。

【讨论】:

  • 我在不同的地方找到了相同的例子(你在上面提供的),我已经完成了。我尝试在现实生活中的上述刮板中实现相同的功能,但仍然无法弄清楚如何使其成功@Pedro Rodrigues。还是谢谢。
  • 把它分成几部分。 1. fuzz,装饰器,返回一个函数。 2. 之前返回的函数,接受你的函数,并返回一个不同的函数来代替它。 3. 代替你返回的函数,接受与你相同的参数(*args, **kwargs 这样做),并在其中调用你的函数。您可以按照代码中的函数定义为 1.、2. 和 3.
  • 函数调用中传递的数字 10 可作为 args[0] 提供给 fuzz_wrapper,因为它已传递给 some_func;如果有帮助的话。
【解决方案2】:

看来我真的很接近我想要实现的目标。我上面帖子中的站点引发连接错误,所以我改用stackoverflow.com。 我的脚本所做的是从其登录页面收集到各种帖子的所有链接,然后从其内页获取每个帖子的标题。

以下代码功能齐全

import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

url = "https://stackoverflow.com/questions/tagged/web-scraping"

def get_links(func):

    def get_target_link(*args):
        titles = []
        for link in func(*args):
            res = requests.get(link)
            soup = BeautifulSoup(res.text,"lxml")
            title = soup.select_one("h1[itemprop='name'] a").text
            titles.append(title)
        return titles
    return get_target_link

@get_links
def get_info(*args):
    ilink = []
    res = requests.get(*args)
    soup = BeautifulSoup(res.text,"lxml")
    for items in soup.select(".summary .question-hyperlink"):
        ilink.append(urljoin(url,items.get('href')))
    return ilink

if __name__ == '__main__':
    for item in get_info(url):
        print(item)

我找不到任何解决方案的一个问题。在下面的函数之间的引用块中,我没有什么可以做的吗?

def get_links(func):
    ###why this area is for? Can I not do anything here as well?
    #### And print that within "get_target_link()" function?
    def get_target_link():

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 1970-01-01
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    • 2018-08-24
    • 2017-03-25
    相关资源
    最近更新 更多