BeautifulSoupを使ってGoogleの表示順位を調べる
BeautifulSoupはHTML解析用のライブラリです。htmllib.HTMLParserやHTMLPaprser.HTMLParserと違い、正しくないHTMLも扱えるようです。これを使ってGoogleの表示順位を調べるスクリプトを書いてみました。
#!python # vim:fileencoding=utf-8 import re import sys import time import urllib2 import urlparse from BeautifulSoup import BeautifulSoup g_url = "http://www.google.co.jp/search?hl=ja&num=100&q=" next_text = u"次へ" interval = 3 client_encoding = "cp932" server_encoding = "utf-8" try: keyword, url = sys.argv[1:] except ValueError: sys.exit() print "keyword:", keyword print "url :", url opener = urllib2.build_opener() opener.addheaders = [('User-agent', 'Mozilla/5.0')] keyword = keyword.decode(client_encoding).encode(server_encoding) search_url = g_url + urllib2.quote(keyword) rank = 0 page = 1 while search_url: print "\rpage : %d" % page, sys.stdout.flush() html = opener.open(search_url).read() soup = BeautifulSoup(html) # HTMLを解析 # Unicode文字列に変換される # キャッシュや関連ページへのリンクを除外したいので # class属性が"l"のAタグだけを取得 for a in soup.findAll("a", {"class":"l"}): rank += 1 href = a["href"] # href属性でURLを取得 # 指定されたURLが見つかれば、結果を表示して終了 if href.startswith(url): # Aタグで囲まれた文字列を取得 title = "".join([c.string for c in a.contents]) print "\nrank :", rank print "href :", href print "title:", title search_url = "" break # 指定したURLが見つからなければ、次の検索結果を調べる else: # 次の検索結果へのリンクを取得 next = soup.find(lambda tag: tag.name=="a" and tag.b and tag.b.string==next_text) if next: search_url = urlparse.urljoin(g_url, next["href"]) page += 1 time.sleep(interval) else: # 次へのリンクが見つからなければ終了 print u"圏外です" search_url = ""
$ python test.py "python インストール" http://www.hlj.com/~tanoue/ keyword: python インストール url : http://www.hlj.com/~tanoue/ page : 2 rank : 158 href : http://www.hlj.com/~tanoue/Python/Mac/mpy00.html title: Mac de Python
HTMLParser.HTMLParserでリンクを抽出
#!python # vim:fileencoding=utf-8 from HTMLParser import HTMLParser import urllib2 from urlparse import urlparse class ExtractTextLinkParser(HTMLParser): def __init__(self): HTMLParser.__init__(self) self.links = [] self.url = "" self.text = "" def handle_starttag(self, tag, attrs): # 開始タグを見つけた場合の処理 if tag == "a": # タグ、属性名は全て小文字 attrs = dict(attrs) # ((属性名, 値), ...) => {属性名:値, ...} if "href" in attrs: self.url = attrs["href"] def handle_endtag(self, tag): # 終了タグを見つけた場合の処理 if tag == "a": if self.text: self.links.append((self.url, self.text)) self.url = self.text = "" def handle_data(self, data): # 開始・終了タグに囲まれた中身の処理 if self.url: self.text += data def get_links(url): response = urllib2.urlopen(url) parser = ExtractTextLinkParser() parser.feed(response.read()) parser.close() links = parser.links return [l for l in links if l[0].find("://") != -1 and not l[0].startswith(url)] links = get_links("http://b.hatena.ne.jp/hotentry") links = [l for l in links if urlparse(l[0])[0]][3:] for url, title in links[:10]: print "[%s:title=%s]" % (url, title.decode("utf-8", "replace"))
テキストエディタでWebサイト構築をガンバル人へ(1/3) − @IT 日本の携帯を高くしている真犯人は 404 Blog Not Found:38歳までに知ることになる、22歳の自分に教えてあげたいたった1つのこと ウェブ制作・プログラマー・デザイナーのためのチートシート集 | コリス やる夫がはてなブックマークを始めたようです。 - 朱雀式 2015年、テレビは「ニコ動」化する?――NRIが示す未来像 (1/2) - ITmedia News パソコン好きが青色申告を体験してみると?:第1回 まずは税金ってナニ? (1/5) - ITmedia Biz.ID らばQ : 42歳までに知ることになる、22歳の自分に教えてあげたい12のこと 「見て欲しい」の本質忘れるな--吉本が語るネット時代の権利者像:コラム - CNET Japan 「真のゆとり教育」が生んだ18歳天才プログラマー トレンド-インタビュー:IT-PLUS
htmllib.HTMLParserでリンクを抽出
#!python # vim:fileencoding=utf-8 from htmllib import HTMLParser from formatter import NullFormatter import urllib2 from urlparse import urlparse class ExtractTextLinkParser(HTMLParser): def __init__(self): HTMLParser.__init__(self, NullFormatter()) self.links = [] def anchor_bgn(self, href, name, type): # <a>が見つかった場合の処理 HTMLParser.anchor_bgn(self, href, name, type) self.save_bgn() # テキストデータの保存を開始 def anchor_end(self): # </a>が見つかった場合の処理 url = self.anchor text = self.save_end() # 保存されたテキストデータを取得 if url and text: self.links.append((url, text)) self.anchor = None def get_links(url): response = urllib2.urlopen(url) parser = ExtractTextLinkParser() parser.feed(response.read()) parser.close() return parser.links links = get_links("http://b.hatena.ne.jp/hotentry") links = [l for l in links if urlparse(l[0])[0]] for url, title in links[5:15]: print "[%s:title=%s]" % (url, title.decode("utf-8", "replace"))
日本の携帯を高くしている真犯人は テキストエディタでWebサイト構築をガンバル人へ(1/3) − @IT ウェブ制作・プログラマー・デザイナーのためのチートシート集 | コリス 404 Blog Not Found:38歳までに知ることになる、22歳の自分に教えてあげたいたった1つのこと Gmailアカウント間でのメール移転方法・複数Gmailアカウントの処理に困っている人に朗報! | Google Mania - グーグルの便利な使い方
HTMLParser.HTMLParserの場合は複数のタグを処理する場合、ifで分岐させる必要がありますが、htmllib.HTMLParserの場合はタグごとにメソッドが用意されています。また、単にURLだけを取得したいならサブクラスを作らなくても可能です。
>>> r = urllib2.urlopen("http://b.hatena.ne.jp/hotentry") >>> p = HTMLParser(NullFormatter()) >>> p.feed(r.read()) >>> p.close() >>> links = p.anchorlist # URLのリストを取得 >>> print "\n".join([l for l in links if urlparse(l)[0]][5:10]) http://www.phs-mobile.com/black/black33.html http://www.atmarkit.co.jp/fwcr/rensai/freeauthoring06/freeauthoring06_1.html http://coliss.com/articles/build-websites/operation/work/796.html http://blog.livedoor.jp/dankogai/archives/50997519.html http://google-mania.net/archives/891
重複する要素を取り除く
>>> xs = [5, 8, 5, 1, 1, 4, 2, 4, 3, 2] >>> set(xs) set([1, 2, 3, 4, 5, 8]) >>> sorted(set(xs), key=xs.index) # 順序を維持 [5, 8, 1, 4, 2, 3]
整数を漢数字に変換
#!python # vim:fileencoding=utf-8 def num2kanji(num): KNUM = [u"", u"一", u"二", u"三", u"四", u"五", u"六", u"七", u"八", u"九"] DIGIT1 = (u"", u"十", u"百", u"千") DIGIT2 = (u"", u"万", u"億", u"兆", u"京") try: num = int(num) except ValueError: raise ValueError("not an integer") max = 10000 ** len(DIGIT2) - 1 if not(0 <= num < max): raise ValueError("not in (0-%d)" % max) if num == 0: return u"零" str_num = str(num) knum = [] for i in xrange(((len(str_num) + 3) / 4)): sn = str_num[-1-i*4:-5-i*4:-1] if sn != "0000": knum.append(DIGIT2[i] + " ") for j, n in enumerate(map(int, sn)): if n != 0: knum.append(DIGIT1[j]) if not(n == 1 and j): knum.append(KNUM[n]) knum.reverse() return "".join(knum).rstrip() while 1: try: print num2kanji(raw_input(">> ")) except ValueError, e: print e except EOFError: break
>> 0 零 >> 1540001 百五十四万 一 >> 43005421003 四百三十億 五百四十二万 千三 >> 224767477905006 二百二十四兆 七千六百七十四億 七千七百九十万 五千六 >> 60093000611220000769 六千九京 三千兆 六千百十二億 二千万 七百六十九 >> -1 not in (0-99999999999999999999) >> 1000000000000000000000000000 not in (0-99999999999999999999) >> 89.97 not an integer
wgetでYouTube等から動画を落とす
今度は正規表現を使わずに書いてみました。
#!python #encoding=utf-8 import urllib import urllib2 import re import os import sys import time save_dir = r"c:\My Documents" interval = 3 def get_video_detail(url): for host, video in VIDEOS.items(): if url.find(host) != -1: return video.get_detail(url) def _extract_from_to(str, from_, to_=None, to_end=False): start = str.find(from_) if start != -1: start += len(from_) end = None if to_: end = str.find(to_, start) if end == -1 and to_end: end = None if end != -1: return str[start:end] VIDEOS = {} class Video: def __init__(self, id_from_to, dl_url_from_to, title_from_to=None, api_url=None, encoding="utf-8", ext=".flv"): self.id_from_to = id_from_to self.dl_url_from_to = dl_url_from_to self.title_from_to = title_from_to self.api_url = api_url self.encoding = encoding self.ext = ext def get_detail(self, url): id = self._extract_id(url) if self.api_url: url = self.api_url % id content = self._get_content(url) params = self._extract_dl_url_params(content) dl_url = self._build_dl_url(params) title = self._extract_title(content) return id, dl_url, title, self.ext def _extract_id(self, url): from_, to_ = self.id_from_to id = _extract_from_to(url.lower(), from_, to_, True) if id: return id else: raise ValueError("invalid video url") def _get_content(self, url): try: response = urllib2.urlopen(url) return response.read() except urllib2.URLError: raise RuntimeError("unable to download video page") def _extract_dl_url_params(self, content): params = [] for from_, to_ in self.dl_url_from_to: p = _extract_from_to(content, from_, to_) if p: params.append(p) else: raise RuntimeError("unable to extract download url") return params def _build_dl_url(self, params): return params[0] def _extract_title(self, content): if self.title_from_to: from_, to_ = self.title_from_to title = _extract_from_to(content, from_, to_) if title: return title.decode(self.encoding, "ignore") class YouTube(Video): def __init__(self): Video.__init__(self, id_from_to = ("/watch?v=", "&"), dl_url_from_to = [("video_id=", "&"), ("&t=", "&")], title_from_to = ("<title>YouTube - ", "</title>") ) def _build_dl_url(self, params): return "http://www.youtube.com/get_video?video_id=%s&t=%s" %\ tuple(params) VIDEOS["youtube.com"] = YouTube() class Veoh(Video): def __init__(self): Video.__init__(self, id_from_to = ("/videos/", "?"), dl_url_from_to = [('fullPreviewHashPath="', '"')], api_url = "http://www.veoh.com/rest/video/%s/details", title_from_to = ('\ttitle="', '"') ) VIDEOS["www.veoh.com"] = Veoh() class Dailymotion(Video): def __init__(self): Video.__init__(self, id_from_to = ("/video/", None), dl_url_from_to = [("&url=", "&")], title_from_to = ('<h1 class="nav with_uptitle">', "</h1>") ) def _build_dl_url(self, params): return urllib.unquote(params[0]) VIDEOS["dailymotion.com"] = Dailymotion() class AmebaVision(Video): def __init__(self): Video.__init__(self, id_from_to = ("movie=", None), dl_url_from_to = [("<imageUrlLarge>", "</imageUrlLarge>")], api_url = "http://vision.ameba.jp/api/get/detailMovie.do?movie=%s", title_from_to = ("\t<title>", "</title>") ) def _build_dl_url(self, params): dl_url = params[0].replace("//vi", "//vm") dl_url = dl_url.replace("/jpg/", "/flv/") dl_url = dl_url.replace("_4.jpg", ".flv") return dl_url VIDEOS["vision.ameba.jp"] = AmebaVision() class Yourfilehost(Video): def __init__(self): Video.__init__(self, id_from_to = ("cat=video&file=", None), dl_url_from_to = [("&videoembed_id=", "&")] ) def _extract_id(self, url): id = Video._extract_id(self, url) return os.path.splitext(id)[0] def _build_dl_url(self, params): return urllib.unquote(params[0]) VIDEOS["www.yourfilehost.com"] = Yourfilehost() invalid_chr_re = re.compile(u'[\/:*?"<>|]') for url in sys.argv[1:]: try: id, dl_url, title, ext = get_video_detail(url) filename = title or id filename = invalid_chr_re.sub(" ", filename) filepath = os.path.join(save_dir, filename + ext) command = "wget -O '%s' --referer='%s' '%s'" %\ (filepath, url, dl_url) os.system(command) time.sleep(interval) except (ValueError, RuntimeError), e: print "Error: %s :%s" % (e, url)