Извлечение лежащих в основе данных через RSelenium со встроенным svg листовки и многое другое - PullRequest
4 голосов
/ 09 февраля 2020

Я хотел бы получить информацию о каждом объявлении в этой ссылке . Теперь я дошел до стадии, когда я могу автоматически щелкнуть See Ad Details, но есть много базовых данных, которые непросто перебрать в аккуратный фрейм данных.

library(RSelenium)
rs <- rsDriver()
remote <- rs$client
remote$navigate(
  paste0(
    "https://www.facebook.com/ads/library/?", 
    "active_status=all&ad_type=political_and_issue_ads&country=US&", 
    "impression_search_field=has_impressions_lifetime&", 
    "q=actblue&view_all_page_id=38471053686"
  )
)

test <- remote$findElement(using = "xpath", "//*[@class=\"_7kfh\"]")
test$clickElement()
## Manually figured out element
test <- remote$findElement(using = "xpath", "//*[@class=\"_7lq0\"]")
test$getElementText()

Выводимый текст сам по себе грязный, но я поверьте, с некоторым временем и усилиями, это может быть превращено во что-то полезное. Проблема состоит в том, чтобы распределить базовые данные на

  1. графике, который выглядит как изображение, и
  2. leafv svg, который отображает данные, когда над ним наведен курсор.

Я в недоумении, как систематически извлекать это изображение и особенно буклет svg. Как мне взять каждое объявление, а затем извлечь в этом случае полные данные, доступные в деталях?

Ответы [ 2 ]

4 голосов
/ 22 февраля 2020

Возраст и пол графика c - это элемент канва. Чтобы получить их как изображения, вы можете сделать скриншот элемента. Python пример:

driver.find_element_by_tag_name('canvas').screenshot("age_and_gender.png")

Где показывалось это объявление - это SVG, и вы можете сохранить его как изображение тем же способом. Результат будет не очень точным, потому что видимая часть SVG и фактическая отличается. Но вы можете обрезать изображение после. Python пример:

driver.find_element_by_tag_name('svg').screenshot("where_this_ad_was_shown.png")

Чтобы извлечь из него полные данные, вы не можете использовать Selenium. Вы можете получить данные, настроив прокси-сервер, перехватить запрос API и получить данные в формате JSON. И да, это возможно.


Простой способ - использовать некоторые запросы для получения объявлений и деталей без Selenium. Python рабочий пример:

import json
import requests

params = (
    ('q', 'actblue'),
    ('count', '1000'), # default is 30, for 38471053686 it will return about 300 results.
    ('active_status', 'all'),
    ('ad_type', 'political_and_issue_ads'),
    ('countries/[0/]', 'US'),
    ('impression_search_field', 'has_impressions_lifetime'),
    ('view_all_page_id', '38471053686'),
)

data = {'__a': '1', }

with requests.session() as s:
    response = s.post('https://www.facebook.com/ads/library/async/search_ads/', params=params, data=data)
    ads = json.loads(response.text.replace('for (;;);', ''))['payload']['results']
    for ad in ads:
        ad_details_params = (
            ('ad_archive_id', ad[0]['adArchiveID']),
            ('country', 'US'),
        )
        response = s.post('https://www.facebook.com/ads/library/async/insights/', params=ad_details_params, data=data)
        print('parse json from response')

Не: Facebook не позволяет автоматизировать сбор данных без письменного разрешения https://www.facebook.com/apps/site_scraping_tos_terms.php

Но, как мы все знаем , Facebook не отказывается собирать наши данные.

Ответ для каждой детали AD будет выглядеть следующим образом:

{
  "__ar": 1,
  "payload": {
    "ageGenderData": [
      {
        "age_range": "18-24",
        "female": 0.03,
        "male": 0.05,
        "unknown": 0
      },
      {
        "age_range": "25-34",
        "female": 0.12,
        "male": 0.12,
        "unknown": 0.01
      },
      {
        "age_range": "35-44",
        "female": 0.16,
        "male": 0.09,
        "unknown": 0
      },
      {
        "age_range": "45-54",
        "female": 0.11,
        "male": 0.05,
        "unknown": 0
      },
      {
        "age_range": "55-64",
        "female": 0.09,
        "male": 0.04,
        "unknown": 0
      },
      {
        "age_range": "65+",
        "female": 0.09,
        "male": 0.03,
        "unknown": 0
      }
    ],
    "currency": "USD",
    "currencyMatched": true,
    "impressions": "35\u00a0B - 40\u00a0B",
    "locationData": [
      {
        "reach": 0,
        "region": "Alabama"
      },
      {
        "reach": 0,
        "region": "Utah"
      },
      {
        "reach": 0,
        "region": "Maine"
      },
      {
        "reach": 0,
        "region": "Louisiana"
      },
      {
        "reach": 0,
        "region": "Kentucky"
      },
      {
        "reach": 0,
        "region": "Kansas"
      },
      {
        "reach": 0,
        "region": "Idaho"
      },
      {
        "reach": 0,
        "region": "Delaware"
      },
      {
        "reach": 0,
        "region": "Connecticut"
      },
      {
        "reach": 0,
        "region": "Arkansas"
      },
      {
        "reach": 0,
        "region": "Hawaii"
      },
      {
        "reach": 0,
        "region": "Alaska"
      },
      {
        "reach": 0,
        "region": "Montana"
      },
      {
        "reach": 0,
        "region": "West Virginia"
      },
      {
        "reach": 0,
        "region": "Vermont"
      },
      {
        "reach": 0,
        "region": "Mississippi"
      },
      {
        "reach": 0,
        "region": "Wyoming"
      },
      {
        "reach": 0,
        "region": "Oklahoma"
      },
      {
        "reach": 0,
        "region": "North Dakota"
      },
      {
        "reach": 0,
        "region": "New Mexico"
      },
      {
        "reach": 0,
        "region": "New Hampshire"
      },
      {
        "reach": 0,
        "region": "Nebraska"
      },
      {
        "reach": 0,
        "region": "Rhode Island"
      },
      {
        "reach": 0,
        "region": "South Dakota"
      },
      {
        "reach": 0.01,
        "region": "Wisconsin"
      },
      {
        "reach": 0.01,
        "region": "Missouri"
      },
      {
        "reach": 0.01,
        "region": "Oregon"
      },
      {
        "reach": 0.01,
        "region": "Minnesota"
      },
      {
        "reach": 0.01,
        "region": "Maryland"
      },
      {
        "reach": 0.01,
        "region": "New Jersey"
      },
      {
        "reach": 0.01,
        "region": "Tennessee"
      },
      {
        "reach": 0.01,
        "region": "Washington, District of Columbia"
      },
      {
        "reach": 0.01,
        "region": "Indiana"
      },
      {
        "reach": 0.02,
        "region": "Michigan"
      },
      {
        "reach": 0.02,
        "region": "Iowa"
      },
      {
        "reach": 0.02,
        "region": "North Carolina"
      },
      {
        "reach": 0.02,
        "region": "Georgia"
      },
      {
        "reach": 0.02,
        "region": "Colorado"
      },
      {
        "reach": 0.02,
        "region": "Ohio"
      },
      {
        "reach": 0.02,
        "region": "Arizona"
      },
      {
        "reach": 0.02,
        "region": "Pennsylvania"
      },
      {
        "reach": 0.02,
        "region": "Virginia"
      },
      {
        "reach": 0.03,
        "region": "Washington"
      },
      {
        "reach": 0.03,
        "region": "Massachusetts"
      },
      {
        "reach": 0.04,
        "region": "Illinois"
      },
      {
        "reach": 0.04,
        "region": "Florida"
      },
      {
        "reach": 0.06,
        "region": "New York"
      },
      {
        "reach": 0.13,
        "region": "California"
      },
      {
        "reach": 0.19,
        "region": "Texas"
      }
    ],
    "singleCountry": "US",
    "spend": "$500 - $599",
    "pageSpend": {
      "currentWeek": null,
      "isPoliticalPage": true,
      "weeklyByDisclaimer": {
        "WARREN FOR PRESIDENT, INC.": 270970
      },
      "lifetimeByDisclaimer": {
        "Elizabeth for MA": 781272,
        "Warren for President": 3396973,
        "": 13584,
        "WARREN FOR PRESIDENT, INC.": 4081618,
        "the Elizabeth Warren Presidential Exploratory Committee": 219471
      },
      "hasPoliticalSpendInAnyCountry": true
    },
    "pageBlurb": "United States Senator from Massachusetts, former teacher, and candidate for President of the United States. (official campaign account)"
  },
  "bootloadable": {},
  "ixData": {},
  "bxData": {},
  "gkxData": {},
  "qexData": {},
  "lid": "6796246259692811543"
}

Наконец, чтобы запустить этот python код из R, используйте reticulate и просто запустите весь сценарий python в виде строки - обратите внимание, что если сценарий python не содержит символов ", очень удобно сразу перейти к R, например,

library(reticulate)
py_run_string("import json
import requests
rest of script etc 
etc 
etc")

Также вам потребуется установить две python библиотеки, которые использует скрипт. Это можно сделать, открыв терминал на ma c и набрав pip install json для установки библиотеки json python и pip install requests для библиотеки запросов)

3 голосов
/ 22 февраля 2020

Это не полный ответ, но, надеюсь, это может помочь.

У меня был go просмотр / разбор, но я не смог разобраться в данных графика, так как он, кажется, находится в сложных местах во многих файлах, доступных через вкладку «сеть» в chrome dev инструменты (я нашел патчи данных, используя команду + f из вкладки сети и поиска слов, содержащихся в графиках, например, «Женщины», «Неизвестно» и т. д. c)

Кому-то, кто знаком с ReactJS, может повезет больше!

Что может сработать

Вы можете попробовать совершенно другой метод, используя оптическое распознавание символов (OCR) ,

То есть, сделайте снимок экрана (то есть remote$screenshot()), преобразуйте из base64 в изображение, прочитайте его, извлеките соответствующую область (то есть местоположения указанных данных c, которые вы ищете), и используйте методы, описанные здесь , чтобы преобразовать области, содержащие данные, которые вы ищете, в текст! (Я буду обновлять, если у меня будет возможность попробовать это, но вряд ли выглядишь, интересно услышать, как вы go)

...