【问题标题】:The whole page is not being parsed with Beautiful Soup整个页面没有被 Beautiful Soup 解析
【发布时间】:2020-12-01 22:46:46
【问题描述】:

我可以从我想要的网站检索前 19 条记录之后的任何内容。鉴于网站上的列表是动态的,我相信这可能与我运行 python 代码时只返回前 19 个有关。我在网上做了一些阅读,但没有找到解决问题的方法。

Bellow 是我的完整 python 代码。我很乐意让社区就我可以做些什么来解决我的问题提出意见。

import requests
from requests import get
from bs4 import BeautifulSoup
import pandas as pd
import numpy as np
import csv


headers = {"Accept-Language": "en-US, en;q=0.5"}
url = "https://www.producthunt.com/time-travel/2019/1/7"
results = requests.get(url, headers=headers) 



soup = BeautifulSoup(results.text, "html.parser")



name = []
description = []
category = []
up_votes = []


ph_project_div = soup.find_all('div', class_='item_54fdd')



for container in ph_project_div:
    
    ph_name = container.a.find('h3', class_='font_9d927 medium_51d18 semiBold_e201b title_9ddaf lineHeight_042f1 underline_57d3c').text
    name.append(ph_name)

    ph_desc = container.a.find('p', class_='font_9d927 grey_bbe43 small_231df normal_d2e66 tagline_619b7 lineHeight_042f1 underline_57d3c').text
    description.append(ph_desc)
    
    
    ph_cat = container.find('span', class_='font_9d927 grey_bbe43 xSmall_1a46e lineHeight_042f1 underline_57d3c')
    category.append(ph_cat)
    
    ph_vote = container.find('span', class_='font_9d927 small_231df semiBold_e201b lineHeight_042f1 underline_57d3c')
    up_votes.append(ph_vote)
    


phunt = pd.DataFrame({
    'Product Name': name,
    'Product Description': description,
    'Product Category': category,
    'Product Votes': up_votes,
})



phunt.to_csv("ph_01_04_2019.csv")

【问题讨论】:

    标签: python web-scraping screen-scraping


    【解决方案1】:

    所以你说 javascript 被用于获取下一组帖子是正确的。这是一个滚动事件触发对服务器的 HTTP 请求的示例。

    要查看此内容,您可以使用检查页面 --> 网络工具 --> XHR。清除所有请求并向下滚动。您将看到一个发布请求,并且在右侧您将看到数据。我倾向于搜索我希望看到的内容以确认数据存在。

    有两种方法可以处理这种类型的动态内容。

    1. 模仿 HTTP 请求
    2. 使用浏览器活动获取数据

    第一个总是更好的选择,如果有潜在的 API 点等。那么最好使用它。数据的效率和准确性很重要。

    第二种选择依赖于像 selenium 这样速度较慢的包,对 HTML 的更改可能非常脆弱,对代码来说很烦人。

    为了收集以下代码的信息,我复制了在网络工具的 XHR 部分中找到的请求的 cURL。我使用一个将其转换为 python 格式的网站,例如 curl.trillworks.com。

    HTTP 请求可能只需要对端点的正确 url 进行简单的 get 请求,在您必须输入 headers/params/data 之前先这样做总是好的。在这个例子中,你得到了所有三个,但实际上你只需要一个用户代理和你想要的响应对象的类型,以及用于引导你获得正确响应的数据。

    代码示例

    import requests
    
    headers = {
        'User-Agent': 'M',
        'content-type': 'application/json',
    }
    
    data = '{"operationName":"Posts","variables":{"year":2019,"month":1,"day":7,"cursor":"MjA=","includeLayout":false},"query":"query Posts($year: Int, $month: Int, $day: Int, $cursor: String) {\\n posts(first: 20, year: $year, month: $month, day: $day, after: $cursor) {\\n edges {\\n node {\\n id\\n ...PostItemList\\n __typename\\n }\\n __typename\\n }\\n pageInfo {\\n endCursor\\n hasNextPage\\n __typename\\n }\\n __typename\\n }\\n}\\n\\nfragment PostItemList on Post {\\n id\\n ...PostItem\\n __typename\\n}\\n\\nfragment PostItem on Post {\\n id\\n _id\\n comments_count\\n name\\n shortened_url\\n slug\\n tagline\\n updated_at\\n ...CollectButton\\n ...PostThumbnail\\n ...PostVoteButton\\n ...TopicFollowButtonList\\n __typename\\n}\\n\\nfragment CollectButton on Post {\\n id\\n name\\n isCollected\\n __typename\\n}\\n\\nfragment PostThumbnail on Post {\\n id\\n name\\n thumbnail {\\n id\\n media_type\\n ...MediaThumbnail\\n __typename\\n }\\n ...PostStatusIcons\\n __typename\\n}\\n\\nfragment MediaThumbnail on Media {\\n id\\n image_uuid\\n __typename\\n}\\n\\nfragment PostStatusIcons on Post {\\n name\\n product_state\\n __typename\\n}\\n\\nfragment PostVoteButton on Post {\\n _id\\n id\\n featured_at\\n updated_at\\n disabled_when_scheduled\\n has_voted\\n ... on Votable {\\n id\\n votes_count\\n __typename\\n }\\n __typename\\n}\\n\\nfragment TopicFollowButtonList on Topicable {\\n id\\n topics {\\n edges {\\n node {\\n id\\n ...TopicFollowButton\\n __typename\\n }\\n __typename\\n }\\n __typename\\n }\\n __typename\\n}\\n\\nfragment TopicFollowButton on Topic {\\n id\\n slug\\n name\\n isFollowed\\n ...TopicImage\\n __typename\\n}\\n\\nfragment TopicImage on Topic {\\n name\\n image_uuid\\n __typename\\n}\\n"}'
    response = requests.post('https://www.producthunt.com/frontend/graphql', headers=headers, data=data)
    data = response.json()
    for a in response.json()['data']['posts']['edges']:
        name = a['node']['name']
        subtext = a['node']['tagline']
        votes = a['node']['votes_count']
        category = a['node']['topics']['edges'][0]['node']['name']
        print('-'*20)
        print('Name: ',name)
        print('Tagline: ',subtext)
        print('Category: ',category)
        print('Votes: ',votes)
    

    输出

    --------------------
    Name:  Newbook Models
    Tagline:  Find & book fashion models online
    Category:  Productivity
    Votes:  57
    --------------------
    Name:  3Leaf Edibles - Quinoa Granola Bite
    Tagline:  Vegan, low dose, high quality edibles.
    Category:  Health and Fitness
    Votes:  57
    --------------------
    Name:  Payfacile
    Tagline:  We simplify access to online payment.
    Category:  Fintech
    Votes:  43
    --------------------
    Name:  Halo by Motorola
    Tagline:  Watch over your baby from above
    Category:  Kids
    Votes:  27
    --------------------
    Name:  Bowflex Max Intelligence
    Tagline:  Bring the benefits of a personal trainer to your home
    Category:  Health and Fitness
    Votes:  23
    

    解释

    标题,你不需要指定user-agent,什么都可以,所以我只使用了一个字符。 content-type 是必要的,但您可以通过试错来发现。

    我不会说谎的数据我没有勇气尝试将其分解,我的猜测是交替使用它可能最终无法为您提供所需的数据。该数据的优点之一是您可以更改年/月/日,它会为您提供需要滚动以触发项目的页面的数据。

    您没有特别问过这个问题,但是使用 f-strings 之类的东西来为每个 requests.get() 调用输入特定数据将是一种解决方法。

    现在你得到的是一个 json 对象,response.json() 方法将这个对象转换为一个 python 字典。所以所有归属于字典的方法都可以使用。

    我强烈建议您查看response.json() 字典,除非您这样做,否则您不会真正了解名称、潜台词的含义。但是大多数 json 对象都是嵌套的数据集,因此您通常想要的数据位于许多键的后面,就像在这种情况下一样。对于我循环访问的每篇文章来获取名称/标语。

    response.json()['data']['posts']['edges'] 列表项的典型输出

    {'node': {'id': '142398',
      '__typename': 'Post',
      '_id': 'UG9zdC0xNDIzOTg=',
      'comments_count': 9,
      'name': 'dinely',
      'shortened_url': '/r/p/142398',
      'slug': 'dinely',
      'tagline': 'Up to 50% off top restaurants, no coupon needed',
      'updated_at': '2020-04-28T16:55:48-07:00',
      'topics': {'edges': [{'node': {'id': '2',
          '__typename': 'Topic',
          'slug': 'android',
          'name': 'Android',
          'isFollowed': False,
          'image_uuid': 'd3e235c7-437b-4ed1-8298-2ce04eded455'},
         '__typename': 'TopicEdge'},
        {'node': {'id': '8',
          '__typename': 'Topic',
          'slug': 'iphone',
          'name': 'iPhone',
          'isFollowed': False,
          'image_uuid': '0ee71650-973d-4933-a3eb-c7201950db4b'},
         '__typename': 'TopicEdge'},
        {'node': {'id': '159',
          '__typename': 'Topic',
          'slug': 'drinking',
          'name': 'Drinking',
          'isFollowed': False,
          'image_uuid': '33a0c652-253d-491f-b86b-4e9ba32ee203'},
         '__typename': 'TopicEdge'},
        {'node': {'id': '250',
          '__typename': 'Topic',
          'slug': 'travel',
          'name': 'Travel',
          'isFollowed': False,
          'image_uuid': '0a49cae9-ccff-47f1-8998-d0f47c2e7775'},
         '__typename': 'TopicEdge'},
        {'node': {'id': '278',
          '__typename': 'Topic',
          'slug': 'e-commerce',
          'name': 'E-Commerce',
          'isFollowed': False,
          'image_uuid': '1aa939fc-89dd-49ae-9fde-d456f9a6c8d2'},
         '__typename': 'TopicEdge'}],
       '__typename': 'TopicConnection'},
      'featured_at': '2019-01-07T03:52:19-08:00',
      'disabled_when_scheduled': True,
      'has_voted': False,
      'votes_count': 58,
      'thumbnail': {'id': '703734',
       'media_type': 'image',
       '__typename': 'Media',
       'image_uuid': '96893366-45e6-477e-9bb8-0e04c2070da6'},
      'product_state': 'default',
      'isCollected': False},
     '__typename': 'PostEdge'}
    

    【讨论】:

    • 你好亚伦。感谢您的详细回复。修改日期范围并运行您提供的代码后,即使脚本运行成功,我也无法返回任何内容。我还尝试了输出的记录数,因为我想捕获每页上的所有记录。这可能是另一个问题,但我想知道在修改日期时,是否需要更改“data =”部分中的任何参数?
    • 我刚刚更改了 'day' 和 'year' 值,并且似乎输出了正确的项目。它只打印由 javascript 调用的那些,即当您滚动然后出现数据时。您是否将我的代码完全复制到您的编辑器中?
    猜你喜欢
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    • 2015-04-27
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多