page = get_page('https://tutsplus.com/authors/gigi-sayfan')
articles = get_page_articles(page)
prefix = 'https://code.tutsplus.com/tutorials'
for a in articles:
print(a[len(prefix):])
Output:
building-games-with-python-3-and-pygame-part-5--cms-30085
building-games-with-python-3-and-pygame-part-4--cms-30084
building-games-with-python-3-and-pygame-part-3--cms-30083
building-games-with-python-3-and-pygame-part-2--cms-30082
building-games-with-python-3-and-pygame-part-1--cms-30081
mastering-the-react-lifecycle-methods--cms-29849
testing-data-intensive-code-with-go-part-5--cms-29852
testing-data-intensive-code-with-go-part-4--cms-29851
testing-data-intensive-code-with-go-part-3--cms-29850
testing-data-intensive-code-with-go-part-2--cms-29848
testing-data-intensive-code-with-go-part-1--cms-29847
make-your-go-programs-lightning-fast-with-profiling--cms-29809
使用 Selenium 动态爬取
静态爬取很适合一系列的文章,但正如我们前面看到的,Disqus 的评论是由 JavaScript 写在一个 iframe 中的。为了获取这些评论,我们需要让浏览器自动与DOM 交互。做这种事情最好的工具之一就是 Selenium。
Selenium 主要用于 Web 应用自动化测试,但它也是一个不错的通用浏览器自动化工具。
安装 Selenium
用这个命令安装 Selenium:pipenv install selenium
选择你的 Web 驱动
Selenium 需要一个 Web 驱动(自动化用的浏览器)。对于网页爬取来说,一般不需要在意选用哪个驱动。我建议使用 Chrome 驱动。Selenium 手册中有相关的介绍。
对比 Chrome 和 PhantomJS
某些情况下你可能想用没有用户界面的(headless)浏览器。理论上来说,PhantomJS 正好就是那款 Web 驱动。但是实际上有人报告一些只会在 PhantomJS 中出现的问题,这些问题在 Selenium 使用 Chrome 或 Firefox 时并不会出现。我喜欢从等式中删除这一变量,使用实际的 Web 浏览器驱动。
统计 Disqus 评论数量
我们来搞点动态抓取,使用 Selenium 统计 Tuts+ 手机的 Disqus 评论数量。下面需要导入的内容。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.expected_conditions import (
presence_of_element_located)
from selenium.webdriver.support.wait import WebDriverWait
get_comment_count()
函数需要传入 Selenium 驱动和 URL 作为参数。它使用驱动的 get()
方法从 URL 获取内容。这和requests.get()
相似,其不同之处在于使用驱动对象管理 DOM 的实时呈现。
然后,它获取教程的标题,并使用 iframe 的父级 id,disqus_thread
,和 iframe 标签来定位 iframe:
def get_comment_count(driver, url):
driver.get(url)
class_name = 'content-banner__title'
name = driver.find_element_by_class_name(class_name).text
e = driver.find_element_by_id('disqus_thread')
disqus_iframe = e.find_element_by_tag_name('iframe')
iframe_url = disqus_iframe.get_attribute('src')
接下来获取 iframe 的内容。注意我们要等到 comment-count
元素出现,因为评论是动态加载的,不一定可用。
driver.get(iframe_url)
wait = WebDriverWait(driver, 5)
commentCountPresent = presence_of_element_located(
(By.CLASS_NAME, 'comment-count'))
wait.until(commentCountPresent)
comment_count_span = driver.find_element_by_class_name(
'comment-count')
comment_count = int(comment_count_span.text.split()[0])
最后部分是返回最新的评论, 当然不包括我自己的评论. 方法是检查我还没有回复的评论.
last_comment = {}
if comment_count > 0:
e = driver.find_elements_by_class_name('author')[-1]
last_author = e.find_element_by_tag_name('a')
last_author = e.get_attribute('data-username')
if last_author != 'the_gigi':
e = driver.find_elements_by_class_name('post-meta')
meta = e[-1].find_element_by_tag_name('a')
last_comment = dict(
author=last_author,
title=meta.get_attribute('title'),
when=meta.text)
return name, comment_count, last_comment
结论
网页爬取是一个非常实用的技术, 尤其当你需要处理的信息浏览器并不提供有用的API支持的时候. 它通常需要一些技巧来从现代web应用中提取信息, 不过一些成熟的、设计良好的工具, 比如: requests、BeautifulSoup、Selenium 都会减轻你的工作并提高效率.
最后, 你可以试一下我写的一些工具, 它们在 Envato Market 有售, 欢迎提问和反馈.
(完)
看完本文有收获?请转发分享给更多人
关注「Python那些事」,做全栈开发工程师