使用Selenium+PhantomJS进行对生成内容的JavaScript网站进行爬取

首先

通过JavaScript生成内容的网站,仅使用BeautifulSoup4通常无法进行网页爬取。
例如,像“当滚动到底部时,下一段内容才会显示”的网站。
由于URL不会发生变化,我们该如何处理呢……。
这时就出现了Selenium+PhantomJS的用法。

Background

出于想要在各种不同情况下都能进行网络爬虫的愿望,本次我们将再次参考《Python网络爬虫与数据采集》这本书进行实践。
简单地说,网络爬虫是一件乐趣无穷的事情。

要做的事情

个别重要信息、比如标题、作者和发布日期。

    • タイトル

 

    • URL

 

    概要

提取,并将其保存在MongoDB、csv和RSS中。

你能做什么呢?

pre.PNG
post.PNG

流动

    1. 使用Selenium和PhantomJS从一个使用JavaScript的网站(本次为“note”)中提取HTML

 

    1. 使用BeautifulSoup4解析提取的HTML,并提取所需信息

将数据保存到MongoDB
将数据保存为csv格式
将数据保存为RSS格式

保護是我們的責任

    • Windows10 64bit

 

    • Python3.7

 

    • MongoDB 4.0.2

 

    • Selenium 3.14.0

 

    • PhantomJS 2.1.1

 

    beautifulsoup4 4.6.3

准备工作

    • PhantomJSをダウンロードし、解凍し、PATHを通しておく必要があります。

参考:「Windowsでphantomjsをインストールしてスクリーンキャプチャ(cygwin実行)」

コードを実行する前にMongoDBをインストールし、起動させておく必要があります。

参考:「Windows版MongoDBのインストール・MongoShellを通してCRUDコマンドを打ってみる」

程式碼 mǎ)

0. 用于日志的咒语

请参考:请停止使用print和import logging来输出日志 – Qiita

from logging import getLogger, StreamHandler, DEBUG
logger = getLogger(__name__)
handler = StreamHandler()
handler.setLevel(DEBUG)
logger.setLevel(DEBUG)
logger.addHandler(handler)
logger.propagate = False

使用Selenium和PhantomJS从JavaScript网站中进行HTML数据抓取。

如果出现 AttributeError: ‘NoneType’对象没有属性 ‘attrs’ 的情况,请将 sleep_time 的数量增加并重新运行。

import sys
import time
from selenium import webdriver # pip install selenium
from selenium.webdriver.support.ui import WebDriverWait
from bs4 import BeautifulSoup

# スクレイピングするサイトのurlを指定
url = 'https://note.mu/'

# 読み込む投稿の多さを指定(amount * 10 投稿程度)
amount = 2

# 読み込み時に待機する時間の指定
sleep_time = 5


def main():
    """
    メイン処理
    """

    # PhantomJS本体のパスを指定
    pjs_path = r"C:\phantomjs-2.1.1\bin\phantomjs.exe"
    driver = webdriver.PhantomJS(executable_path=pjs_path)

    # ページの読み込み
    navigate(driver, url, amount=amount, sleep_time=sleep_time)

    # データの抽出
    posts = scrape_posts(driver, url)

    return posts


def navigate(driver, url, amount=1, sleep_time = 5):
    """
    目的のページに遷移する
    amount >= 1
    """

    logger.debug('Navigating...')
    driver.get(url)
    assert 'note' in driver.title

    # 指定した回数分、ページ下部までスクロールしてコンテンツの生成を待つ
    for i in range(1, amount+1):
        driver.execute_script('scroll(0, document.body.scrollHeight)')
        logger.debug('Waiting for contents to be loaded...({0} times)'.format(i))
        time.sleep(sleep_time)


def scrape_posts(driver, url):
    """
    投稿のURL、タイトル、概要のdictをリスト形式で取得
    """

    posts = []

    # Seleniumで取得したHTMLをBeautifulSoup4に読み込む
    html = driver.page_source
    bsObj = BeautifulSoup(html,"html.parser")

    for post_html in bsObj.findAll("div",{"class":"c-card__body"}):
        # 記事のURLを取得
        content_url = post_html.find("h3").find('a').attrs['href']
        content_full_url = url + content_url[1:]

        # 記事タイトルを取得
        title = post_html.find("h3").find('a').find('span').get_text()
        title = title.replace('\n', '') # 改行を削除

        # 記事の概要を取得
        try:
            description = post_html.find("p", {'class':'p-cardItem__description'}).get_text()
            description = description.replace('\n', '') # 改行を削除
        except AttributeError as e:
            description = '-no description-'
            logger.debug("「{0}」 has no description: {1}".format(title, e))

        posts.append({
            'url': content_full_url,
            'title': title,
            'description': description,
            })

    return posts

执行

posts = main()

将数据保存到MongoDB中。

在命令提示符上创建数据库目录。

mongod --dbpath "${データベース用のディレクトリへのパス}"

执行。先启动MongoDB。

from pymongo import MongoClient, DESCENDING # pip install pymongo

# MongoDBとの接続
mongo_client = MongoClient('localhost', 27017) # MongoDBと接続
db = mongo_client.note
collection = db.recomend # noteデータベース -> recomendコレクション
collection.delete_many({}) # 既存の全てのドキュメントを削除しておく

def save_to_mongodb(collection, items):
    """
    MongoDBにアイテムのリストを保存
    """

    result = collection.insert_many(items) # コレクションに挿入
    logger.debug('Inserted {0} documents'.format(len(result.inserted_ids)))

# 実行
save_to_mongodb(collection, posts)

用csv格式保存数据

可以将数据保存在CSV文件中,这样非工程师的人也可以使用Excel进行排序和分析,非常方便。
还可以省去安装MongoDB的麻烦。
因此,在这里

    • MongoDBを経由する場合

 

    MongoDBを使用しない場合

我会提供两种方法。

因为所创建的csv文件的字符编码是utf-8,所以在Excel中普通打开会出现乱码。请按照以下网站上的步骤来打开:
“在Excel中打开utf-8的CSV文件而不改变字符编码的方法”

import csv

def save_as_csv(posts, csv_name):
    # 列名(1行目)を作成
    ## [タイトル、URL、概要]
    col_name = ['title', 'url', 'description']

    with open(csv_name, 'w', newline='', encoding='utf-8') as output_csv:
        csv_writer = csv.writer(output_csv)
        csv_writer.writerow(col_name) # 列名を記入

        # csvに1行ずつ書き込み
        for post in posts:
            row_items = [post['title'], post['url'], post['description']]
            csv_writer.writerow(row_items)
# =====
# MongoDBを経由する場合
# from pymongo import MongoClient, DESCENDING # pip install pymongo
# mongo_client = MongoClient('localhost', 27017) # MongoDBと接続
# db = mongo_client.note
# collection = db.recomend # noteデータベース -> recomendコレクション
# posts = collection.find()

# MongoDBを使用しない場合
# postsはmain()の返り値
# =====

# ファイル名を指定
csv_name = 'note_list.csv'

save_as_csv(posts, csv_name)

以RSS格式保存数据。

import feedgenerator # pip install feedgenerator

def save_as_feed(f, posts):
    """
    コンテンツリストをRSSフィードとして保存
    """

    feed = feedgenerator.Rss201rev2Feed(
        title='おすすめノート',
        link='https://note.mu/',
        description='おすすめノート')

    for post in posts:
        feed.add_item(title=post['title'], link=post['url'],
                     description=post['description'], unique_id=post['url'])

    feed.write(f, 'utf-8')

# postsは「3. csv形式でデータを保存」を参照
with open('note_recommend.rss', 'w', encoding='utf-8') as f:
    save_as_feed(f, posts)

总结

在一些看似无法确定如何进行网页爬取的站点上,当我们滚动到最底部时,下一个内容就会显示出来。但是通过使用Selenium和PhantomJS,我们发现可以进行网页爬取。

请参照以下内容。

代码主要参考了以下内容:2 3。

    『Pythonクローリング&スクレイピング―データ収集・解析のための実践開発ガイド―』技術評論社

我参考了自己以前的文章。

    • 「小説家になろう」をPythonでスクレイピングして本文を保存する(自然言語処理用コーパス作成) – Qiita

 

    YouTubeのAPIで取得した輝夜月さんの動画情報をMongoDBやpandasで触ってみた – Qiita

GitHub: 代码托管平台

我把 Jupyter Notebook 公开在 GitHub 上。
https://github.com/kokokocococo555/JavaScriptScraping-demo

课题

    • 「1. SeleniumとPhantomJSでJavaScriptサイトからHTMLをスクレイピング」実行時にAttributeError: ‘NoneType’ object has no attribute ‘attrs’が出ることがあるが、原因は未解明。

一応の対処法も記載しているが、どこまで効果があるのやら…。
(2018-09-18追記)エラーが出る理由が書籍のサポートページに載っていた(「P.198, 5.6.2 noteのおすすめコンテンツを取得する」)。対応策も本記事と同じく、time.sleep(2)を入れるという方法。これ以上は放置、という方向でしょうか。

请注意

在进行网页爬虫时,需要注意版权等问题。
请参考以下文章,意识到规则和礼仪。

    • Webスクレイピングの注意事項一覧 – Qiita

 

    Webスクレイピングの法律周りの話をしよう! – Qiita

相关文章【网络爬虫和自动化】

基本的なスクレイピング(BeautifulSoup4)

「小説家になろう」をPythonでスクレイピングして本文を保存する(自然言語処理用コーパス作成) – Qiita

APIを利用したスクレイピング・データベースへの保存(MongoDB)・データ分析の一歩(pandas)

YouTubeのAPIで取得した輝夜月さんの動画情報をMongoDBやpandasで触ってみた – Qiita

JavaScriptを用いたサイトのスクレイピング(Selenium, PhantomJS)

JavaScriptでコンテンツが生成されるサイトのスクレイピング【Selenium+PhantomJS】 – Qiita

メール通知(smtplib)・定期的なプログラムの自動実行(タスクスケジューラ)

「小説家になろう」で新しく公開された記事のメール通知を自動化【Python】 – Qiita

自動ログイン(Selenium, ChromeDriver)

QiitaにSeleniumで自動ログインして全投稿を定期的にバックアップ【Python】 – Qiita

在我参考的书籍中,使用Selenium进行解析和信息提取,但是在我的环境中无法成功,所以我使用熟悉的BeautifulSoup4进行处理。↩

由于note的HTML结构与书籍出版时不同,我自己编写了网页抓取的代码。↩

在本文中,我加入了一些改进措施,例如”可以指定要抓取的帖子数量”。↩