【问题标题】:Using Scrapy to scrape a site that uses XMLHttpRequests to load its content使用 Scrapy 抓取使用 XMLHttpRequests 加载其内容的站点
【发布时间】:2019-04-17 19:23:26
【问题描述】:

我尝试抓取的网站(在浏览器中)一次返回 50 个职位。当我尝试使用 scrapy 返回所有职位时,它只会抓取其中的 20 个。

网页链接: https://recruiting.ultipro.com/COM1038/JobBoard/d22da326-8928-4fbc-8b3b-99b6db355d5d/?q=&o=postedDateDesc

进入此页面后,我意识到内容是动态呈现的,因此我右键单击并检查了该页面,然后在开发工具中打开 XHR > Network 选项卡重新加载它。它显示了两个文件

  1. 获取过滤器
  2. 加载机会

'LoadOpportunities' 似乎很有希望,所以我查看了 XHR 选项卡下的“响应”,它返回了一个似乎包含我想要的所有信息的 JSON 文件。我使用 CTRL-f 搜索“标题”并找到 50 次出现。完美!

然后来测试它。我打开 Scrapy Shell 并查看了返回 JSON 的页面:

https://recruiting.ultipro.com/COM1038/JobBoard/d22da326-8928-4fbc-8b3b-99b6db355d5d/JobBoardView/LoadOpportunities

当您在浏览器中打开此页面时,它仅返回(当前)54 个职位空缺中的 20 个。当我使用 json 包和 scrapy shell 抓取页面时,我第一次发现了这一点。

当您使用我在“LoadOpportunities”文件下发布的第一个链接查看浏览器中的“预览”选项卡时,您可以看到它在“机会”键下方有多达 50 个值。我正在尝试从其中的每一个中刮取值“标题”。

我还创建了一个抓取工具,它使用 HTML 响应做同样的事情,但它并不是特别有用。它没有完成工作,因为它实际上并没有与动态网页交互,只是初始页面连接到的部分 JSON 文件。

import scrapy, re, json, requests
from ..items import MetroSouthItems

class MetroSouth(scrapy.Spider):
    name = "metrosouth"
    start_urls = [
    'https://recruiting.ultipro.com/COM1038/JobBoard/d22da326-8928-4fbc-8b3b-99b6db355d5d/JobBoardView/LoadOpportunities',
    ]

    def parse(self, response):
        html_res = response.body
        decoded_res = str(html_res,"utf-8")
        json_file = open("metrosouth.json", "w")
        json_file.write(decoded_res)
        with open("metrosouth.json") as json_data:
            data = json.load(json_data)
            items = MetroSouthItems()
            for i in range(len(data["opportunities"])):
                items["job_title"] = data["opportunities"][i]["Title"]
                print(i)
                yield items

我想抓取所有可用的作业,然后在某个时候将它们铲到数据库中/可能让蜘蛛每天早上重新运行,以便我可以跟踪新列表。现在我让它用新列表覆盖一个 JSON 文件。

如果有人对前进有任何提示或指导,我将不胜感激。我感觉它与 Javascript 有关,因为它确实说“LoadOpportunities”是由 jslib 发起的,但我对 javascript 没有太多经验,也不知道这意味着什么。

【问题讨论】:

    标签: python json ajax scrapy


    【解决方案1】:

    在最初的页面中,他们使用一些额外的有效负载发出 POST 请求。我们可以用这样的方式重现它:

    import scrapy, json
    
    
    class MetroSouth(scrapy.Spider):
        name = "metrosouth"
        search_url = url = 'https://recruiting.ultipro.com/COM1038/JobBoard/d22da326-8928-4fbc-8b3b-99b6db355d5d/JobBoardView/LoadOpportunities'
    
        def start_requests(self):
            payload = """{"opportunitySearch":{"Top":100,"Skip":0,"QueryString":"","OrderBy":[{"Value":"postedDateDesc","PropertyName":"PostedDate","Ascending":false}],"Filters":[{"t":"TermsSearchFilterDto","fieldName":4,"extra":null,"values":[]},{"t":"TermsSearchFilterDto","fieldName":5,"extra":null,"values":[]},{"t":"TermsSearchFilterDto","fieldName":6,"extra":null,"values":[]}]},"matchCriteria":{"PreferredJobs":[],"Educations":[],"LicenseAndCertifications":[],"Skills":[],"hasNoLicenses":false,"SkippedSkills":[]}}"""
            yield scrapy.Request(self.search_url, method='POST', body=payload)
    
        def parse(self, response):
            j = json.loads(response.text)
            print '======'
            for i, row in enumerate(j.get('opportunities')):
                print i, ' - ', row.get('Title')
            print '======'
    

    检查有效载荷中的参数Top。最初它设置为 50,请求页面为我们提供了 20 条记录。但是我已经将它增加到 100,现在我得到了所有 54 条记录。 希望它会有所帮助。

    【讨论】:

    • 我还没有亲自尝试过,但我会相信你的话。非常感谢!我确实尝试在一些单独的代码中发送有效载荷,但我无法让它工作。这听起来像是一个愚蠢的问题,但你怎么知道页面何时需要有效负载或其他类型的参数@vezunchik?
    • 新代码对我不起作用。我没有复制和粘贴您的代码,但我确实对其进行了调整以配合 ypour 解决方案。我将在上面添加我的代码的更新版本以供审查。如果您发现我做错了什么,请告诉我。
    猜你喜欢
    • 1970-01-01
    • 2013-05-09
    • 2020-10-12
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 2020-09-17
    • 2015-08-17
    • 1970-01-01
    相关资源
    最近更新 更多