9
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

Pythonでスクレイピング→Twitterで投稿してみた 2018/11/12現在

目的

Pythonを使ってスクレイピング(見出しなど)してきて、Twitterで投稿する。
(今更だがTwitter APIを使ってみたかった)

参考記事

追記

  • 2019/3/28 ツイートをするpostメソッドの引数**kwargsについて更新しました。

スクレイピング

ヤ〇ーニュースから情報を取得してくる

BeautifulSoupテスト
#ヤ〇ーニュースから情報持ってくる
target_url = 'https://~~~~~~co.jp/'
#プロキシ設定が必要なら引数にproxiesを入れておく
req = requests.get(target_url,proxies=proxy_settings.proxy_dict)
soup = BeautifulSoup(req.text,"html.parser")

`req.text`だと日本語が文字化けするときがある`req.content`にすると解消した

#欲しい部分をfind_allで抽出する
newslist=soup.find_all('ul', attrs={'class': 'listFeed'})
linklist=list.find_all(href=re.compile("https://~~~~~"))

↑を実行したらエラーになった

エラー内容
AttributeError: ResultSet object has no attribute 'find_all'. 
You're probably treating a list of items like a single item. 
Did you call find_all() when you meant to call find()?

調べたところ、どうやらfind_allの入れ子はできないらしい。。。
ということで、以下のコードで実行!!

修正➀
newslist=soup.find_all('ul', attrs={'class': 'listFeed'})
title=news.find("a").find("dt").text -->見出し
link=news.find("a").attrs["href"]    -->リンク

一応ほしい情報は持ってこれた。
ただ、find()だと1件だけとってきて終了になる。。。
更に修正した。

修正➁
newslist=soup.select(".listFeedWrap")

#コードが冗長になっている
for i in range(len(newslist)):
    title=newslist[i].find(attrs={"class":"titl"}).text  -->見出し
    link=newslist[i].find("a").attrs["href"]             -->リンク

#こちらのほうが望ましい
for news in newslist:
    title=news.find(attrs={"class":"titl"}).text         -->見出し
    link=news.find("a").attrs["href"]                    -->リンク

これで見出しとリンクがちゃんととれた!
for文の記載はfor i in range(~だと冗長らしい。

ちなみに
修正➀では、newslist=soup.find_all('ul', attrs={'class': 'listFeed'})だが
修正➁では、newslist=soup.select(".listFeedWrap")にしました。

➀のfind_all()だと、戻り値がelement.Resultset型で、
➁のselect()だと、戻り値がlist型になる。

いろいろ試したが、element.Resultset型だとfor文を使ったときに細かく情報が取れなかった。
(listの1番目の要素にhtmlが全部入ってるイメージ)
対して、list型だと1番目の要素は「html」になっており、欲しい部分を上手く取れた。

Twitter

TwitterAPIを利用して、つぶやくことが目的。

まずはTwitter Developerアカウントを作った。
ページ上部の参考記事にもあるが、こちらを参考に作成した。

アクセストークンなどを取得して、OAuth認証をした

OAuth認証
from requests_oauthlib import OAuth1Session
import proxy_settings

class OAuth認証:

    #トークンの取得
    key=consumer_key
    secret=consumer_secret
    token=access_token
    token_secret=access_token_secret
    auth = OAuth1Session(key, secret, token, token_secret)  #OAuth認証

認証がうまくいったので、投稿してみる

ツイッター投稿
def post(self,text):
        #tweet内容をポスト
        url="https://api.twitter.com/1.1/statuses/update.json"
        param = {"status" : text}
        res=self.auth.post(url,params=param)

        #post出来たかどうか
        if res.status_code == 200:
            print("Posted:"+text)
        else:
            print("Failed : %d"% res.status_code)

投稿できない!!プロキシにはじかれているっぽい。
実はここが結構はまったところ。

ライブラリのソースを見たら、
requestsライブラリだとrequests.get(url, proxies=~~~)でプロキシ突破して接続できた。
ただ、OAuth認証のメソッドが引数にkeysecret_keyしかとらない
→つまりTwitterのアクセストークンが入らないから使えない、、、

一方でrequest_oauthlibライブラリだとOAuth1Session(key, secret, token, token_secret)でトークンも含めて認証できた。
しかし、投稿をしてくれるpost メソッドにproxiesという引数がない、、、
→認証できてもプロキシ突破できず、投稿できなくね?

そこで、postメソッドの定義をみたら、引数に**kwargsなるものが。なんやそれ?
:param \*\*kwargs: Optional arguments thatrequesttakes.
他に引数増やしてもいいよってことらしい。
ということで、引数にproxies=~~を足してみた。

postメソッド定義
def post(self, url, data=None, json=None, **kwargs):
        r"""Sends a POST request. Returns :class:`Response` object.

        :param url: URL for the new :class:`Request` object.
        :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
        :param json: (optional) json to send in the body of the :class:`Request`.
        :param \*\*kwargs: Optional arguments that ``request`` takes.
         ↑requestメソッドの引数にあるオプションは追加していいですよのこと
        :rtype: requests.Response
        """
        return self.request('POST', url, data=data, json=json, **kwargs)

~2019/3/28追加~ **kwargsについて 

**kwargsって何者なんだと思い、requestメソッドを見てみると↓だった。

requestメソッド

def request(self, method, url,
            params=None, data=None, headers=None, cookies=None, files=None,
            auth=None, timeout=None, allow_redirects=True, proxies=None,
            hooks=None, stream=None, verify=None, cert=None, json=None):
         """
         ~途中省略~
         :param proxies: (optional) Dictionary mapping protocol or protocol and
            hostname to the URL of the proxy.
         """

Dictionary型でプロキシ情報渡せば良いらしい。
下の感じで作りました。

プロキシdict

#プロキシ設定
proxy_dict = {
    "http": "http://hogehoge",
    "https": "https://hogehoge"
}

というわけでpost実行時にproxies=プロキシdictを渡すと、、、
無事に投稿できました!!!
いやぁ~長かった。
ちゃんとできて一安心です。

BeautifulSoupについて少し調べたので↓に書きます。

BeautifulSoupとは

Pythonで使えるライブラリで
対象URLのサイトからhtmlをとってこれる優れもの。
いろんな要素・属性を指定できる。

かなりいろいろなことが出来るっぽいが、そこまで詳しく調べてない。。

BeautifulSoup API

説明 コード例
子要素 soup.head
タグ全検索 soup.find_all('li')
1件検索 soup.find('li')
属性検索 soup.find('li', href="")
class検索 soup.find('a', class_="first")
属性取得 first_link_element['href']
テキスト要素 first_link_element.string
親要素 first_link_element.parent

BeautifulSoup cssセレクタ チートシート

説明 コード例
タグ検索 soup.select('li')
1件検索 soup.select_one('li')
属性検索 soup.select('a[href=""]')
属性存在 soup.select('a[data])
class検索 soup.select('a.first')

ちなみに、pprint.pprint(news.__dict__)を実行すると詳しい内容が出てくる。

import pprint

print(type(news))
pprint.pprint(news.__dict__)
実行結果
<class 'bs4.element.Tag'>
{'attrs': {'class': ['listFeedWrap']},
 'can_be_empty_element': False,
 'contents': ['\n',.....],
 'hidden': False,
 'known_xml': False,
 'name': 'li',
 'namespace': None,
 'next_element': '\n',
 'next_sibling': '\n',
 'parent': <ul class="listFeed">.....],
 'parser_class': <class 'bs4.BeautifulSoup'>,
 'prefix': None,
 'preserve_whitespace_tags': {'pre', 'textarea'},
 'previous_element': '\n',
 'previous_sibling': '\n'}

まとめ

意外と時間がかかったが、面白かった!!
次はスクレイピング→ディープラーニングにもっていきたい。

他人の記事を探してやってみるのもいいけど、やっぱり定義をみれば一発でわかりますね!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
9
Help us understand the problem. What are the problem?