質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.61%

  • Tkinter

    138questions

    Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Pythonスクレイピング

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 310

T_www

score 5

 前提・実現したいこと

Yahoo画像検索を使用して画像を集めまくるプログラムを作成したいのです。※超初心者です。

 発生している問題

bs4で画像のURLを取得する方法が解らないです。

 該当のソースコード

# -*- coding: utf-8 -*-
#おまじない
import urllib #URLエンコード用
import bs4 #HTMl解析
import requests #URlアクセス?
import re #正規表現(URL抽出)
import tkinter as tk #GUI
#import ui #GUI作成

#GUIコールバック関数
def owari():
    quit()

#関数コーナー
def worde(): #検索ワード要求からURL作成まで(仮) 使用ライブラリ:urllib
    #検索したいワード
    ken = "草"
    #URLエンコード
    enk = urllib.parse.quote(ken)
    #URL作成
    global url
    url = 'https://search.yahoo.co.jp/image/search?n=60&p=' + enk
    url = url + '&search.x=1'
#ダウンロード用(HTML)関数
def hack(): #wordeで取得したURLから画像のURLを抜き出す(解析) 使用ライブラリ:bs4
    res = requests.get(url) #URLアクセス?
    res.raise_for_status() #わかんねえ
    soup = bs4.BeautifulSoup(res.text, "html.parser") #わかんね
    elems = soup.select('a') #aタグを選択
    url_list = [] #URLを格納するリストやで
    for img in elems: #URL取得やで
        url_list.append(img.get('href'))
        print (url_list)

    kazu = (len(url_list)) #判定

    print (kazu)

    if kazu == 0: #URL数が0だった時、エラーを出す。
        error = "urlzeroerror"
    else:
        tdo = url_list
worde()
hack()

#GUIやで
#トップウィンドウ作成
root = tk.Tk()
#ウィンドウのタイトルを設定
root.title("フリー画像ダウンローダ -ver1.0_beta")
#ウィンドウの大きさを指定
root.geometry("800x500")

#UI部分(本体)
frame1 = tk.Frame(root)
frame1.pack()
#ロゴ?を表示
image1 = tk.PhotoImage(file = 'LOGO.gif')
tk.Label(frame1, image = image1).pack()#.grid(row=0, column=0)
#探すボタンを作成
serch = tk.Button(frame1, text='探す', padx=45, pady=7)#, command=b1)
serch.pack()
#空白
tk.Label(frame1, text="").pack()
#オプションボタンを作成
option = tk.Button(frame1, text='設定', padx=44, pady=7) #微調整しているため、padxの値がちょっとおかしい
option.pack()
#空白
tk.Label(frame1, text="").pack()
#バグ連絡
bug = tk.Button(frame1, text="バグ・要望等連絡", padx=15, pady=7)
bug.pack()
#空白
tk.Label(frame1, text="").pack()
#終了ボタンを作成
owa = tk.Button(frame1, text="終了", padx=46, pady=7, command=owari)
owa.pack()

root.mainloop()

 試したこと

いろいろなサイトを見て回ったこと

 補足情報

Pyhon3.6.5

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

bs4で画像のURLを取得する方法が解らないです。

これに関しては質問文のソースコードでurl_listでURLを取得できているので、ダウンロードの仕方と保存の仕方が分からないと判断しました。

極論を言えば以下のコードでも可能かと。

import os

for s in url_list:
    res = requests.get(s)
    file_name = os.path.basename(s)
    with open(file_name, 'wb') as f:
        f.write(res.content)

※超初心者です。

うーん、少し悩みましたが、ソースコードを頑張って読み取ってくれることを期待して。

クラス 役割
MyFrame 画面
DownLoader ダウンロード処理
# -*- coding: utf-8 -*-
from concurrent.futures import ThreadPoolExecutor, as_completed
from io import BytesIO
import os
from pathlib import Path
from threading import Thread
from time import sleep
#おまじない
import urllib #URLエンコード用
import bs4 #HTMl解析
import requests #URlアクセス?
import re #正規表現(URL抽出)
import tkinter as tk #GUI
#import ui #GUI作成


class MyFrame(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.pack()

        # ロゴ?を表示
        # image1 = tk.PhotoImage(file = 'LOGO.gif')
        # tk.Label(frame1, image = image1).pack()#.grid(row=0, column=0)
        # ※検索キーワードのテキストボックスを作成
        self.key_word = tk.StringVar(value='草')
        self.txt_key_word = tk.Entry(self, textvariable=self.key_word)
        self.txt_key_word.pack()
        # 探すボタンを作成
        serch = tk.Button(self, text='探す', padx=45, pady=7, command=self.search)
        serch.pack()
        # 空白
        tk.Label(self, text="").pack()
        # オプションボタンを作成
        option = tk.Button(self, text='設定', padx=44, pady=7)  # 微調整しているため、padxの値がちょっとおかしい
        option.pack()
        # 空白
        tk.Label(self, text="").pack()
        # バグ連絡
        bug = tk.Button(self, text="バグ・要望等連絡", padx=15, pady=7)
        bug.pack()
        # 空白
        tk.Label(self, text="").pack()
        # 終了ボタンを作成
        owa = tk.Button(self, text="終了", padx=46, pady=7, command=self.owari)
        owa.pack()

    def search(self):
        search_word = self.key_word.get()
        # ※画面をブロックさせないためにスレッドを生成して、startを呼び出す。
        t = Thread(target=hack, args=(search_word,))
        t.start()

    def owari(self):
        quit()


class DownLoader(object):
    def __init__(self):
        # ミリ秒に変換
        self.interval = 500 / 1000
        # sec
        self.timeout = 10
        self.download_dir = Path('download')
        self.download_dir.mkdir(exist_ok=True)

    def get(self, url: str, params: dict=None):
        # 実際のリクエスト処理はここ
        res = requests.get(url, params=params, timeout=self.timeout)
        print(res.url)
        # http ステータスコードが200番台以外なら例外を発生させる。
        # 存在しないwebページをurlに指定すると例外が発生するので分かりやすいかと。
        res.raise_for_status()
        return res

    def get_content(self, url: str, params: dict=None):
        sleep(self.interval)
        res = self.get(url, params)
        return BytesIO(res.content), res.headers['content-type']

    def request(self, url_list: list) ->None:
        """
           internet -- (Get) --> local
           use ThreadPoolExecutor
        """
        count = 0
        with ThreadPoolExecutor() as executor:
            future_to_url = {executor.submit(self.get_content, url): url for url in url_list}
            for future in as_completed(future_to_url):
                url = future_to_url[future]
                try:

                    # get_contentの戻り値はここで取得
                    buffer, content_type = future.result()
                    # 保存対象のファイルかどうか。
                    if not self.save_content_type(content_type):
                        continue
                    # 保存ファイル名はURLのパス部分をそのまま取得
                    # 重複が発生するので連番を付けたりして対応してくださいな。
                    file_name = self.download_dir / os.path.basename(url)

                    print(content_type, file_name)
                    # 保存
                    self.save_file(buffer, file_name)
                    count += 1
                except Exception as ex:
                    print(f"url:{url}, {ex}")

        if count == 0:
            print(f'save file Empty')

    def save_content_type(self, content_type: str) ->bool:
        is_saved = ["image/jpeg", "image/png", "image/gif"]
        return content_type.lower() in is_saved

    def save_file(self, buffer: BytesIO, file_name: Path) ->None:
        with file_name.open('wb') as f:
            f.write(buffer.getvalue())


# ※Downloaderクラスのインスタンスを生成
dl = DownLoader()


#ダウンロード用(HTML)関数
def hack(search_word: str): #wordeで取得したURLから画像のURLを抜き出す(解析) 使用ライブラリ:bs4
    url = 'https://search.yahoo.co.jp/image/search'
    params = {'n': '60', 'p': search_word, 'search.x': '1'}
    res = dl.get(url, params)
    print(res.text)
    soup = bs4.BeautifulSoup(res.text, "html.parser") #わかんね
    elems = soup.select('a') #aタグを選択
    url_list = [] #URLを格納するリストやで
    for img in elems: #URL取得やで
        url_list.append(img.get('href'))
        #print (url_list)
    print(url_list)
    kazu = (len(url_list)) #判定

    #print (kazu)

    if kazu == 0: #URL数が0だった時、エラーを出す。
        error = "urlzeroerror"
        # ※即座に抜けて、if文のネストを減らす
        return
    tdo = url_list
    # ※リスト内の重複を削除
    url_list = list(set(url_list))
    # ※url_listの内容に対してリクエスト!
    dl.request(url_list)


def main() ->None:
    root = tk.Tk()
    # ウィンドウのタイトルを設定
    root.title("フリー画像ダウンローダ -ver1.0_beta")
    # ウィンドウの大きさを指定
    root.geometry("800x500")
    f = MyFrame(root)
    root.mainloop()


if __name__ == '__main__':
    main()


◇参考情報

  1. 17.4. concurrent.futures – 並列タスク実行
  2. MIME タイプ

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/22 16:19

    柔軟性を向上すべく
    params = {'n': '60', 'p': "草", 'search.x': '1'}
    の部分の"草"を変数にしてみましたが、うまく動きません。
    エラーは出ないのですが、どうすれば動くのでしょう...(泣

    キャンセル

  • 2018/04/22 16:39

    >T_wwwさんへ
    どういう処で詰まってるのか、分かりませんでしたが、tk.StringVarとtk.Entryを使った画面から検索キーワードを渡すサンプルコードです。変更差分ですが、私が書いたコードをPyCharmのLocal History機能で差分が分かるかと。PyCharmのCommunity Editionは無料なので、導入することをお勧め致します。

    キャンセル

  • 2018/04/22 17:34 編集

    Entryのワードを変えて見たところ
    Exception in thread Thread-1:
    Traceback (most recent call last):
    File "C:\Users\xxxx\AppData\Local\Programs\Python\Python36-32\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
    File "C:\Users\xxxx\AppData\Local\Programs\Python\Python36-32\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
    TypeError: hack() takes 1 positional argument but 4 were given
    のようになりました。どのようなことなのでしょう
    google先生に翻訳してもらったんです。↓

    402/5000
    スレッドの例外Thread-1:
    トレースバック(直近の最後のコール):
    ファイル "C:¥Users¥xxxx¥AppData¥Local¥Programs¥Python¥Python36-32¥lib¥threading.py"、行916、_bootstrap_inner
    self.run()
    ファイル "C:\ Users \ xxxx \ AppData \ Local \ Programs \ Python \ Python36-32 \ lib \ threading.py"、行864、実行中
    self._target(* self._args、** self._kwargs)
    TypeError:hack()は1つの位置引数を取るが、4つは与えられた

    キャンセル

  • 2018/04/22 22:18

    >T_wwwさんへ
    質問文を編集して現状のソースコードを貼ってくださいな。

    キャンセル

  • 2018/04/22 22:20

    あ、あの、このページのソースコードです(教えてくれたやつです)。

    キャンセル

  • 2018/04/22 22:22

    うーんと、手順としては画面のテキストボックスを変更して探すボタンをクリックですよね?

    キャンセル

  • 2018/04/22 22:23

    そうです。

    キャンセル

  • 2018/04/22 22:26

    回答文のコードのdef hack(search_word: str):こうなってるのでー
    MyFrameのdef search(self):が
    search_word = self.key_word.get()
    t = Thread(target=hack,args=search_word)
    こうなのですがー

    キャンセル

  • 2018/04/22 22:26

    あ、変数 tのしたに t.start()がありますが

    キャンセル

  • 2018/04/22 22:31

    と、話がそれてしまいました、質問のエラーメッセージとしてはt = Thread(target=hack,args=search_word) の部分でargsオプションに引数を渡してますが、関数:hackの定義が引数1個しかないというメッセージです。

    キャンセル

  • 2018/04/22 22:33

    def search(self):
    search_word = self.key_word.get()
    print(len(search_word))
    このprintが1になると思いますが。

    キャンセル

  • 2018/04/22 22:35

    あああ、入力キーワードに, (カンマ)が入ってませんか?

    キャンセル

  • 2018/04/22 22:36

    いや、違いますね、なんとなく再現したので、直してみます。失礼しました。

    キャンセル

  • 2018/04/22 22:36

    入れておりません(´・ω・`)

    キャンセル

  • 2018/04/22 22:38

    def search(self):
    search_word = self.txt_key_word.get()
    # ※画面をブロックさせないためにスレッドを生成して、startを呼び出す。
    t = Thread(target=hack, args=(search_word,))
    t.start()

    キャンセル

  • 2018/04/22 22:40

    ><
    修正してみましたが、どーでしょうか?お手数をかけてすみませぬ。

    キャンセル

  • 2018/04/22 22:44

    す、すごい!こんな初心者を手伝ってくれてありがとうございました。

    キャンセル

  • 2018/04/22 22:48

    で、バグ(不具合)を入れちゃったのですが、原因は引数argsの渡しかたでした。
    tupleをargsに渡す必要があったのが、文字列を渡して、文字列の長さになっていたと思います。

    args=(search_word,)
    args=search_word

    キャンセル

  • 2018/04/22 22:54

    なるほど(凄い

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Tkinter

    138questions

    Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。