Как я могу очистить страницу с динамическим содержимым (созданным JavaScript) в Python?
Я пытаюсь разработать простой веб-скребок. Я хочу извлекать обычный текст без HTML-разметки. Мой код работает с обычным (статическим) HTML, но не тогда, когда содержимое генерируется JavaScript, встроенным в страницу.
В частности, когда я использую urllib2.urlopen(request)
для чтения содержимого страницы, оно не показывает ничего, что могло бы быть добавлено кодом JavaScript, потому что этот код нигде не выполняется. Обычно это запускается веб-браузером, но это не является частью моей программы.
Как я могу получить доступ к этому динамическому содержимому из моего кода Python?
Смотрите также можно ли использовать scrapy для очистки динамического содержимого с веб-сайтов, использующих AJAX? ответы, относящиеся к Scrapy.
Переведено автоматически
Ответ 1
РЕДАКТИРОВАТЬ сентябрь 2021: phantomjs
также больше не поддерживается
РЕДАКТИРОВАТЬ 30 / Dec / 2017: этот ответ отображается в лучших результатах поиска Google, поэтому я решил его обновить. Старый ответ все еще находится в конце.
dryscape больше не поддерживается, и разработчики dryscape рекомендуют использовать только библиотеку Python 2. Я обнаружил, что использование библиотеки python от Selenium с Phantom JS в качестве веб-драйвера достаточно быстро и просто для выполнения работы.
После установки Phantom JS убедитесь, что phantomjs
двоичный файл доступен по текущему пути:
phantomjs --version
# result:
2.1.1
# Пример
Чтобы привести пример, я создал пример страницы со следующим HTML-кодом. (ссылка):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Javascript scraping test</title>
</head>
<body>
<p id='intro-text'>No javascript support</p>
<script>
document.getElementById('intro-text').innerHTML = 'Yay! Supports javascript';
</script>
</body>
</html>
без javascript написано: No javascript support
и с помощью javascript: Yay! Supports javascript
# Очистка без поддержки JS:
import requests
from bs4 import BeautifulSoup
response = requests.get(my_url)
soup = BeautifulSoup(response.text)
soup.find(id="intro-text")
# Result:
<p id="intro-text">No javascript support</p>
# Очистка с поддержкой JS:
from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get(my_url)
p_element = driver.find_element_by_id(id_='intro-text')
print(p_element.text)
# result:
'Yay! Supports javascript'
Вы также можете использовать библиотеку Python dryscrape для очистки веб-сайтов, управляемых javascript.
# Очистка с поддержкой JS:
import dryscrape
from bs4 import BeautifulSoup
session = dryscrape.Session()
session.visit(my_url)
response = session.body()
soup = BeautifulSoup(response)
soup.find(id="intro-text")
# Result:
<p id="intro-text">Yay! Supports javascript</p>
Ответ 2
Мы не получаем правильных результатов, потому что любое содержимое, сгенерированное javascript, должно быть отображено в DOM. Когда мы извлекаем HTML-страницу, мы извлекаем начальный, неизмененный javascript, DOM.
Поэтому нам нужно отобразить содержимое javascript перед обходом страницы.
Поскольку selenium уже много раз упоминается в этой теме (и также упоминалось, насколько медленно он иногда работает), я перечислю два других возможных решения.
Решение 1: Это очень хороший учебник о том, как использовать Scrapy для обхода содержимого, сгенерированного javascript, и мы собираемся следовать именно этому.
Что нам понадобится:
На нашем компьютере установленDocker. До этого момента это было плюсом по сравнению с другими решениями, поскольку оно использует платформу, независимую от операционной системы.
Установите Splash, следуя инструкции, указанной для нашей соответствующей ОС.
Цитирую документацию splash:Splash - это служба рендеринга javascript. Это легкий веб-браузер с HTTP API, реализованный на Python 3 с использованием Twisted и QT5.
По сути, мы собираемся использовать Splash для рендеринга содержимого, сгенерированного Javascript.
Запустите сервер splash:
sudo docker run -p 8050:8050 scrapinghub/splash
.Установите плагин scrapy-splash:
pip install scrapy-splash
Предполагая, что у нас уже есть созданный Scrapy-проект (если нет, давайте создадим один), мы будем следовать руководству и обновлять
settings.py
:Затем перейдите в свой проект scrapy
settings.py
и установите эти промежуточные программы:DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}URL сервера Splash (если вы используете Win или OSX, это должен быть URL компьютера docker: Как получить IP-адрес контейнера Docker от хоста?):
SPLASH_URL = 'http://localhost:8050'
И, наконец, вам также нужно установить эти значения:
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'Наконец, мы можем использовать
SplashRequest
:В обычном spider у вас есть объекты запроса, которые вы можете использовать для открытия URL-адресов. Если страница, которую вы хотите открыть, содержит данные, сгенерированные JS, вы должны использовать SplashRequest (или SplashFormRequest) для рендеринга страницы. Вот простой пример.:
class MySpider(scrapy.Spider):
name = "jsscraper"
start_urls = ["http://quotes.toscrape.com/js/"]
def start_requests(self):
for url in self.start_urls:
yield SplashRequest(
url=url, callback=self.parse, endpoint='render.html'
)
def parse(self, response):
for q in response.css("div.quote"):
quote = QuoteItem()
quote["author"] = q.css(".author::text").extract_first()
quote["quote"] = q.css(".text::text").extract_first()
yield quoteSplashRequest отображает URL-адрес в виде html и возвращает ответ, который вы можете использовать в методе обратного вызова (parse).
Решение 2: На данный момент (май 2018 г.) давайте назовем это экспериментальным...
Это решение предназначено только для Python версии 3.6 (на данный момент).
Вы знаете модуль requests (а кто его не знает)?
Теперь у него есть младший брат для обхода веб-страниц: requests-HTML:
Эта библиотека предназначена для того, чтобы сделать синтаксический анализ HTML (например, очистить веб) максимально простым и интуитивно понятным.
Запросы на установку-html:
pipenv install requests-html
Сделайте запрос к URL страницы:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get(a_page_url)Визуализируйте ответ, чтобы получить сгенерированные Javascript биты:
r.html.render()
Наконец, модуль, похоже, предлагает возможности очистки.
В качестве альтернативы мы можем попробовать хорошо документированный способ использования BeautifulSoup с r.html
объектом, который мы только что визуализировали.
Ответ 3
Возможно, selenium сможет это сделать.
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
Ответ 4
Если вы когда-либо раньше использовали Requests
модуль для python, я недавно узнал, что разработчик создал новый модуль под названием Requests-HTML
который теперь также имеет возможность отображать JavaScript.
Вы также можете посетить https://html.python-requests.org / чтобы узнать больше об этом модуле, или, если вас интересует только рендеринг JavaScript, вы можете посетить https://html.python-requests.org/?#javascript-support чтобы непосредственно узнать, как использовать модуль для рендеринга JavaScript с помощью Python.
По сути, после правильной установки Requests-HTML
модуля следующий пример, который показан по ссылке выше, показывает, как вы можете использовать этот модуль для очистки веб-сайта и визуализации JavaScript, содержащегося на веб-сайте:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('http://python-requests.org/')
r.html.render()
r.html.search('Python 2 will retire in only {months} months!')['months']
'<time>25</time>' #This is the result.
Недавно я узнал об этом из видео на YouTube. Нажмите здесь! чтобы посмотреть видео на YouTube, в котором демонстрируется, как работает модуль.