PythonでWebスクレイピングツールを自作してみる➀

Python

はじめに

Webスクレイピングというもをやってみたく、折角ならwebアプリにしようといことで作ってみました。

自分のPC(Windows10)内部に環境を構築し、Pythonを起動させています。

実際に完成した(いや、まだ未完成)動画がこちらです。

Web Scraping With Python V2 – web情報自動取得ツール –

機能紹介

  1. 検索キーワードでスクレイピングする
  2. 検索した日付のデータのみ表示する
  3. 検索キーワードでフィルタリングする
  4. 画面上のデータをダウンロードする
  5. 検索キーワードでデータを削除する

上記の機能を実装しました。

Python3のダウンロード

まずは環境構築です。今、自分の環境にPythonが入っているか確認してみましょう。

こちらの記事にあるようにコマンドプロンプトなどでバージョン情報が表示されれば、すでにPCにインストールされています。しかし、本記事ではバージョン3系を利用していますので、バージョンが古い場合は新しバージョンをダウンロードしましょう。

Pythonのダウンロードは公式ページから行います。

ダウンロード方法の詳細はこちら

MySQLのインストール

MySQLインストールからテーブル作成はこちら

bottleフレームワークダウンロード

Pythonのプレームワークであるbottleを準備します。

この記事にもありますがbottleはpipでンストールするよりもコピペでファイル作ったほうが初心者的には、余計なところで躓かずに済むかもしれません。

さて、pythonのフレームワークといってもさまざまなものがありますが、今回はWeb上の情報を取ってきて、DBに登録、ブラウザに表示するということがやりたかったので、一番無駄がなさそうなbottleを採用しました。

このフレームを利用することで簡単にWEBアプリケーション開発を行えるようになります。

フロントを作る

フロントではざっくり以下の技術を使いました。

SPAとはSingle Page Applicationのことで、 単一のWebページでアプリケーションを構成する設計構造の名称 のことです。

ページ遷移がなく動作の向上が期待できます。近年ではVue.jsなどのフレームワークを使って実装されることが多く、より高度なWEB表現が可能になるらしいです。

今回はSPAで実装をしたかったのでjQueryでAjaxを使って実装しました。

Ajaxとは 「Asynchronous JavaScript + XML 」の略称でサーバーとの非同期通信を可能にします。

ソースコード

top.html

<!DOCTYPE html>
<html lang="ja">
<head>
 <!-- Required meta tags -->
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 
 <link rel="icon" href="./static/img/ico/favicon.ico">

 <!-- Bootstrap CSS -->
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
 
<title>Web Scraping with Python</title> 

</head>
<body>

  <!-- nav配置 -->
  
  <nav class="navbar navbar-expand-md  navbar-light shadow sticky-top" style="background-color: rgb(1, 1, 1);">
    <div class="navbar-brand"><img src="./static/img/ico/images.png" width="70" height="60" alt="ロゴ"></div>
      <div class="d-inline-block mr-5" style="font-style: italic;color: aliceblue;font-size:xx-large ;font-weight: 700;">
         Web Scraping with Python
      </div>
    <div class= "m-2 mt-3">
      <div class="row">
        <div class="float-right col-md">
          <div class="d-inline-block">
            <div class="m-2 ml-5">
              <button type="button" class="btn btn-success btn-lg shadow" data-toggle="modal" data-target="#exampleModal" style="font-weight: 600;">SCRAPING START</button>
            </div>
          </div>
          <div class="d-inline-block">
            <div class="m-2">
              <div class="other" id="all">
                <a href="#" class="btn btn-primary btn-lg shadow" role="button" style="font-weight: 600;">TODAY'S DATA</a>
              </div>
            </div>
          </div>
          <div class="d-inline-block">
            <div class="m-2">
              <div class="dropdown">
                <select id="ddmenu" class="btn btn-warning btn-lg shadow" style="font-weight: 600;">
                  KEY WORDS
                </select>
              </div>
            </div>
          </div>
          <div class="d-inline-block">
            <div class="m-2">
              <div class="csv">
                <a href="#" class="btn btn-secondary btn-lg shadow"  role="button" onclick="output();return false;">CSV DOWNLOAD</a>
              </div>
            </div>
          </div>
          <div class="d-inline-block">
            <div class="m-2">
                <button type="button" id="del" class="btn btn-danger btn-lg shadow" style="font-weight: 600;">DELETE</button>              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </nav>

  <!-- データ配置 -->

  <div class="container" >
    
    
    <!-- データ配置 -->

    <div class="display-area">
      <div class="m-2 mt-3">
        <table class="table">
          <thead>
            <tr>
              <th></th>
              <th></th>
            </tr>
          </thead>
          <tbody id="table">
          </tbody>
        </table>
        <div id="iimg"></div>
      </div>
      <!-- モーダル部分始まり -->
  
      <div class="modal fade" id="delete" tabindex="-1" role="dialog" aria-labelledby="delete" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title" id="delete">Notice</h5>
              <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">×</span>
              </button>
            </div>
            <div class="modal-body">delete</div>
            <div class="modal-footer">
              <button type="button" id="godel" class="btn btn-secondary" data-dismiss="modal">OK</button>
            </div>
          </div>
        </div>
      </div>


      <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">×</span>
              </button>
            </div>
            <div class="modal-body">
                <div class="form-group">
                  <label for="recipient-name" class="col-form-label"><b>Scraping key word</b></label>
                  <input type="text" class="form-control" id="key">
                </div>
                <div class="modal-footer">
                  <button type="button" class="btn btn-secondary" data-dismiss="modal">CANCEL</button>
                  <button type="button" id="start" class="btn btn-primary" data-dismiss="modal">START</button>
                </div>
            </div>
          </div>
        </div>
      </div>
      <!-- モーダル部分終わり -->
    </div>
  </div>
 <footer class="fixed-bottom" style="background-color: rgb(1, 1, 1);">
  <div style="font-style: italic;color: aliceblue;font-size:larger;">© 2020 KEI inc. v2.0</div>
 </footer>
  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>

  <!--独自js-->
  <script src="/static/js/spa.js"></script>
  </body>
</html>

spa.js

//グローバル変数
var items = [];
var tUrl = 'http://localhost:8085/';
var all = 'all';
var key = 'key';
var sflag = 0;
var savedata;

function today() {
  var d = new Date();
  var formatted = `${d.getFullYear()}-${(d.getMonth()+1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
  return formatted;
}

function scraping(sendkey) {  
  $(function(){
    var targetUrl = tUrl+'scraping';

    console.log(sendkey);
    var request = {
      'sendkey': sendkey
    };
      $.ajax({
        url: targetUrl,
        type: 'POST',
        contentType: 'application/JSON',
        //dataType: 'JSON',
        data : JSON.stringify(request),
        scriptCharset: 'utf-8',
      }).done(function(data){ 
          console.log(data);
          $('#table').empty();
          $('#iimg').empty();
            $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #FF3300;font-size:xx-large ;font-weight: 700;">FINISH</div></td></tr>');
            var pastDate = null; 
            other(all,pastDate);
          sflag = 0;
          getPastDay();
        }).fail(function(data, XMLHttpRequest, textStatus) {
          console.log(data);
          $('#table').empty();
          $('#iimg').empty();
          $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #FF3300;font-size:xx-large ;font-weight: 700;">FAILURE</div></td></tr>');
          sflag = 0;
          console.log("XMLHttpRequest : " + XMLHttpRequest.status);
          console.log("textStatus     : " + textStatus);
      });
  });
}

function other(other,pastDate) {

  var targetUrl = tUrl+'other';
  var date = null;

  if (pastDate == null || pastDate == '') {
    date = today();  
  } else {
    date = pastDate;
  }

  if (date != null) {
    if (other == key) {
      var request = {
          'date' : date,
          'other': key
      };
    } else if(other == all){
      var request = {
        'date' : date,
        'other': all
        };
    }
  } else {
    alert('不正な値');
    var request = {
      'date' : today(),
      'other': all
    };
  }

  $(function() {
      $.ajax({
          url: targetUrl,
          type: 'POST',
          contentType: 'application/JSON',
          dataType: 'JSON',
          data : JSON.stringify(request),
          scriptCharset: 'utf-8',
      }).done(function(data) { 
          if (data == null || data == '' || data[0] == '') {
            $('#table').empty();
            $('#iimg').empty();
            $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">NO DATA</div></td></tr>');
          } else {
            show(data);
            savedata = data;
          }
        }).fail(function(data, XMLHttpRequest, textStatus) {
          console.log(data);
          console.log("XMLHttpRequest : " + XMLHttpRequest.status);
          console.log("textStatus     : " + textStatus);
      });
  });
}

function getPastDay() {  
  $(function(){
    var targetUrl = tUrl+'getPastDay';    
      $.ajax({
          url: targetUrl,
          type: 'POST',
          contentType: 'application/JSON',
          dataType: 'JSON',
          data : null,
          scriptCharset: 'utf-8',
      }).done(function(data) { 
          if (data == null || data == '' || data[0] == '') {
            $('#table').empty();
            $('#iimg').empty();
            $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">NO DATA</div></td></tr>');
          } else {
            $('#ddmenu').empty(); 
            $("#ddmenu").append('<option value="">KEY WORDS</option>');
            for (var i = 0; i < data.length; i++) {
              $("#ddmenu").append('<option value="'+data[i].dt+'"style="font-weight: 600;" >'+data[i].dt+'</option>');
            }
          }
        }).fail(function(data, XMLHttpRequest, textStatus) {
          console.log(data);
          console.log("XMLHttpRequest : " + XMLHttpRequest.status);
          console.log("textStatus     : " + textStatus);
      });
  });
}

function delwords() {  
  $(function(){
    var targetUrl = tUrl+'getPastDay';    
      $.ajax({
          url: targetUrl,
          type: 'POST',
          contentType: 'application/JSON',
          dataType: 'JSON',
          data : null,
          scriptCharset: 'utf-8',
      }).done(function(data) { 
          if (data == null || data == '' || data[0] == '') {
            $('#table').empty();
            $('#iimg').empty();
            $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">NO DATA</div></td></tr>');
          } else {
            $('#table').empty(); 
            for (var i = 0; i < data.length; i++) {
              //console.log(data);
              $('#table').append('<tr><td>'+data[i].dt+'</td><td><button type="button" id="'+data[i].dt+'" class="godel btn-danger" class="btn btn-primary">DELETE</button></td></tr>');
            }
          }
        }).fail(function(data, XMLHttpRequest, textStatus) {
          console.log(data);
          console.log("XMLHttpRequest : " + XMLHttpRequest.status);
          console.log("textStatus     : " + textStatus);
      });
  });
}

function godelwords(sendkey) {  
  $(function(){
    var targetUrl = tUrl+'del';
    console.log(sendkey);
    var request = {
      'sendkey': sendkey
    };
      $.ajax({
        url: targetUrl,
        type: 'POST',
        contentType: 'application/JSON',
        //dataType: 'JSON',
        data : JSON.stringify(request),
        scriptCharset: 'utf-8',
      }).done(function(data){ 
          console.log(data);
          $('#table').empty();
          $('#iimg').empty();
            $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #FF3300;font-size:xx-large ;font-weight: 700;">FINISH</div></td></tr>');
            var pastDate = null; 
            other(all,pastDate);
          sflag = 0;
          getPastDay();
        }).fail(function(data, XMLHttpRequest, textStatus) {
          console.log(data);
          $('#table').empty();
          $('#iimg').empty();
          $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</div></td><td><div style="font-style: italic;color: #FF3300;font-size:xx-large ;font-weight: 700;">FAILURE</div></td></tr>');
          sflag = 0;
          console.log("XMLHttpRequest : " + XMLHttpRequest.status);
          console.log("textStatus     : " + textStatus);
      });
  });
}

////////////////////////////////////////////////////////////////////////////////////////////////
//CSV download
//jsonをcsv文字列に編集する
function jsonToCsv(json, delimiter) {
  var header = Object.keys(json[0]).join(delimiter) + "\n";
  var body = json.map(function(d){
      return Object.keys(d).map(function(key) {
          return d[key];
      }).join(delimiter);
  }).join("\n");
  return header + body;
}

//csv変換
function exportCSV(items, delimiter, filename) {

  //文字列に変換する
  var csv = jsonToCsv(items, delimiter);

  //拡張子
  var extention = delimiter==","?"csv":"tsv";

  //出力ファイル名
  var exportedFilenmae = (filename  || 'export') + '.' + extention;
  //文字化け対策
  var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);

  //BLOBに変換
  var blob = new Blob([bom,csv], { type: 'text/csv;charset=utf-8;' });

  if (navigator.msSaveBlob) { // for IE 10+
      navigator.msSaveBlob(blob, exportedFilenmae);
  } else {
      //anchorを生成してclickイベントを呼び出す。
      var link = document.createElement("a");
      if (link.download !== undefined) {
          var url = URL.createObjectURL(blob);
          link.setAttribute("href", url);
          link.setAttribute("download", exportedFilenmae);
          link.style.visibility = 'hidden';
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
      }
  }
}


function output(){
    console.log(savedata);
    if (savedata == null || savedata == 0) {
      alert("No data");
    } else {
      var filename = savedata[0].dt;;
      exportCSV(savedata,',', filename);
    }
    
}
////////////////////////////////////////////////////////////////////////////////////////////////
window.onload = function() {
  //today
  var pastDate = null; 
  other(all,pastDate);
  getPastDay();
}

//scraping
$(function(){ 
  $('#start').on('click',function(){
    if (sflag == 0) {
      sflag = 1;
      sendkey= $("#key").val();
      scraping(sendkey);
      $('#table').empty();
      $('#iimg').empty();
      $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</td><td><div style="font-style: italic;color: #0000FF;font-size:xx-large ;font-weight: 700;">RUNNING <img src="./static/img/ico/load.gif" width="30" height="30" /></div></td></tr>');
    } else {
      $('#table').empty();
      $('#iimg').empty();
      $('#table').append('<tr><td><div style="font-style: italic;color: #000000;font-size:xx-large ;font-weight: 700;">INFO</td><td><div style="font-style: italic;color: #0000FF;font-size:xx-large ;font-weight: 700;">RUNNING NOW ( PLEASE WAIT A MINUTE ) <img src="./static/img/ico/load.gif" width="30" height="30" /></div></td></tr>');
    }  
  });
});

//TODAY
$(function() {
  $('.other').on('click',function() {
    var id = $(this).attr('id');
    var pastDate = null;
    if (id == all) {
      other(all,pastDate);
    } 
  });
});

//プルダウン選択時
$(function() {
  $('#ddmenu').on('click',function() {
    var pastDate = $("#ddmenu").val();
    other(key,pastDate);
  });
});

$(function() {
  $('#del').on('click',function() {
    delwords();
  });
});

$(function() {
  $(document).on('click','.godel',function() {
    var sendkey =  $(this).attr("id");
    window.confirm("データは完全に削除されます。本当によろしいですか?");
    console.log('sendkey');
    console.log(sendkey);
    godelwords(sendkey);
  });
});

function show(data) {
  $(function() {
    
    $('#table').empty();
    $('#iimg').empty();
    for (var i = 0; i < data.length; i++) {
      
    var dirDay = data[i].dt;
    var dirNaeme = dirDay.replace( /-/g , "" );
    dirNaeme = dirNaeme.substr(0,8);

      var id = i+1;
      $('#table').append('<tr><td>'+data[i].img_id+'</td><td><a href='+data[i].url+' target="_blank" style="font-size:large;">'+data[i].title+'</a></td><td> ('+data[i].dt+')</td></tr>');
    }  
  });
  return data;
}

バックエンドを作る

Pythonを使ってコーディングしていきます。

スクレイピングするためにseleniumを導入しましょう。

こちらの記事を参考にseleniumの導入を行いました。

ソースコード

main.py

# coding:utf-8
from bottle import request, route, get, post, hook, response, static_file, template, redirect, run
from selenium import webdriver 
import mysql.connector
import datetime
import os.path
import random
import string 
import random
import time
import json
import os


#status
PASTDAY = 'pastday'
ALL = 'all'
KEY = 'key'
DEL = 'del'

#ファイルパス
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_DIR = os.path.join(BASE_DIR, 'static')

#JS
@route('/static/js/<filename:path>')
def send_static_js(filename):
    return static_file(filename, root=f'{STATIC_DIR}/js')

#img
@route('/static/img/<filename:path>')
def send_static_img(filename):
    return static_file(filename, root=f'{STATIC_DIR}/img')

#rootの場合
@route("/")
def index():
    return template('top')

@post('/other')
def postOther():
    #値取得
    data = request.json
    date = data['date']
    qerytype = data['other']

    url = dbconn(qerytype, date)
    #ID NULLチェック
    if isUrlCheck(url):
        print('checkedUrl:')
        #json作成
        jsonUrl = makeJson(url)
        print(type(jsonUrl))
        return jsonUrl
    else:
        return postOther()

@post('/getPastDay')
def pastDay():
    date = None
    qerytype = PASTDAY
    url = dbconn(qerytype, date)
    #ID NULLチェック
    if isUrlCheck(url):
        print('checkedUrl:')
        #json作成
        jsonUrl = makeJson(url)
        print(type(jsonUrl))
        return jsonUrl
    else:   
        return postOther()

@post('/scraping')
def startscraping():
    #値取得
    data = request.json
    sendkey = data['sendkey']

    scraping(sendkey)
    
    
@post('/del')
def delete():
    #値取得
    data = request.json
    sendkey = data['sendkey']
    print(sendkey)
    
    qerytype = DEL
    dbconn(qerytype, sendkey)

i = 0
def isUrlCheck(url):
    if url == None:
        print('チェックNG')
        global i
        i += 1
        print('i')
        print(i)
        if i < 5:
            return None 
        else:
            return True
    else:
        print('チェックOK')
        return True

def makeJson(url):
    jsonUrl = jsonDumps(url)
    return jsonUrl
    
def jsonDumps(url):
    url = json.dumps(url)
    return isTypeCheck(url)

def isTypeCheck(jsonUrl):
    if type(jsonUrl) is str:
        return jsonUrl
    else:
        jsonDumps(jsonUrl)
    

def dbconn(qerytype, date):
    print("q")
    print(qerytype)
    print(date)

    f = open('./conf/prop.json', 'r')
    info = json.load(f)
    f.close()
    #DB設定
    
    conn = mysql.connector.connect(
            host = info['host'],
            port = info['port'],
            user = info['user'],
            password = info['password'],
            database = info['database'],
    )
    
    cur = conn.cursor(dictionary=True)   
    
    try:    
        #接続クエリ
        if qerytype == ALL:
            sql = "SELECT site_id,title,url,img_id,CAST(dt AS CHAR) as dt FROM scrapingInfo2 WHERE dt LIKE '"+date+'%'"' ORDER BY dt DESC"
        elif qerytype == KEY:
            sql = "SELECT site_id,title,url,img_id,CAST(dt AS CHAR) as dt FROM scrapingInfo2 WHERE img_id LIKE '"+date+"'ORDER BY dt DESC"
        elif qerytype == PASTDAY:
            sql = "SELECT DISTINCT img_id as dt FROM scrapingInfo2 ORDER BY dt DESC"
        elif qerytype == DEL:
            sql = "DELETE FROM scrapingInfo2 where img_id = '"+date+"'"
        
            print(sql)

        #クエリ発行
        if qerytype == DEL:
            cur.execute(sql)
            conn.commit()
        else:
            cur.execute(sql)
            cur.statement    
            url = cur.fetchall()

        if url is not None:
            return url
        else:
            return None
    except:
        print("DBエラーが発生しました")
        return None
    finally:
        cur.close()
        conn.close()

def scraping(sendkey):
    print("scraping start")
    print(sendkey)
    driver = webdriver.Chrome(BASE_DIR+'./static/chromedriver.exe')
    driver.get('https://www.google.com/')
 
    search = driver.find_element_by_name('q')
    
    search.send_keys(sendkey) 
    search.submit() 
    time.sleep(3)     

    i = 1
    i_max = 5
    try:
        while i <= i_max:
            class_group = driver.find_elements_by_class_name('r')
            for elem in class_group:
                title = elem.find_element_by_class_name('LC20lb').text 
                url = elem.find_element_by_tag_name('a').get_attribute('href') 

                #DB設定
                f = open('./conf/prop.json', 'r')
                info = json.load(f)
                f.close()
                conn = mysql.connector.connect(
                    host = info['host'],
                    port = info['port'],
                    user = info['user'],
                    password = info['password'],
                    database = info['database']
                )
                now = datetime.datetime.now()
                dt = "{0:%Y-%m-%d %H:%M:%S}".format(now)
                c = conn.cursor()
                #データ登録
                sql = "INSERT INTO scraping.scrapingInfo2(site_id,title,url,img_id,dt) VALUES (2,%s,%s,%s,%s)"
                print(sql)
                c.execute(sql, (title, url, sendkey, dt))
                sql = 'SET @i := 0' 
                c.execute(sql)
                sql = 'UPDATE `scraping`.`scrapingInfo2` SET id = (@i := @i +1);'
                c.execute(sql)
                conn.commit()
                conn.close()

            if driver.find_elements_by_id('pnnext') == []:
                i = i_max + 1
            else:
                next_page = driver.find_element_by_id('pnnext').get_attribute('href')
                driver.get(next_page)   
                i = i + 1               
                time.sleep(3) 
    except:
        driver.quit()
        print("DBエラーが発生しました")    
    finally:
        # ブラウザを閉じる
        driver.quit()  
              
if __name__ == "__main__":
    run(host='localhost', port=8085, reloader=True, debug=True)

長いですね。もっとシンプルに書けそうですが、google先生に頼りながら、なんとかやりたいことはできました。

時間があるときにコードをきれいにします。

main.pyを実行するとブラウザからlocalhostでアクセス可能になります。

コードに何が書いてあるかは察してください。

課題

  • chromeのドライバーがアップデートされると動作しなくなる(自動的にChromeのバージョンを検出し、バージョンにあったドライバーをダウンロードする機能が必要)
  • find_element_by_class_nameでHtmlタグ中の’LC20lb’をキーワードにタイトルを抽出していますが、うまく動作しない場合があります。(おそらく、一番初めのリンクが広告で始まる場合→まだデバックしていない)
  • Delete機能にバグあり

追加したい機能

  • 検索されたURL毎にお気に入り登録
  • お気に入りしたURLのみをフィルターしcsv出力

スポンサーリンク

コメント

  1. […] […]

タイトルとURLをコピーしました