python爬蟲抓取app列表的圖標
爬蟲簡介
所謂的爬蟲簡單來說,就是通過不斷的變化http請求的url,向服務器進行請求,從而獲得服務器返回的相關數據,在這些數據中提取對自己有用的信息。
爬蟲的步驟
- 構造url。根據自己想要抓取的信息,構造出相應的url。
- 請求url。根據上面構造的url,向服務器發起請求。(在python中可以用urllib、request庫等)
- 提取數據。向服務器發起請求后,服務器會返回相關的數據(如:html、json、xml),在這些數據中提取對自己有用的信息(可以用正則表達式、BeautifulSoup、json、lxml等)。
- 保存數據。已特定的格式(csv文件)保存上面提取的數據,以便于后面進行數據分析。
例子
使用爬蟲抓取app列表的圖標,如微信、QQ、今日頭條等。
- 構造url
打開豌豆莢首頁,在搜索框中輸入微信,進行搜索。豌豆莢服務器返回下圖的頁面,有搜索到app列表的信息和圖標。
在進行搜索時,用Wireshark進行抓包,可以看到在搜索微信時,對應的http請求為:http://www.wandoujia.com/search?key=%E5%BE%AE%E4%BF%A1&source=index
QQ的請求為:
http://www.wandoujia.com/search?key=QQ&source=index
今日頭條的請求為:
http://www.wandoujia.com/search?key=%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1&source=index
- 從上面的3個不同的app的http請求,我們可以發現,只是搜索的key值有所不同,比如微信的key為%E5%BE%AE%E4%BF%A1,QQ的key為QQ,今日頭條的key為%E5%BE%AE%E4%BF%A1。這些值對應字符的utf-8編碼 + url編碼。也是說微信的utf-8為E5 BE AE E4 BF A1,格式化為url編碼就是:%E5%BE%AE%E4%BF%A1。
- 所以對于不同的app應用要構造出不同請求的url,我們只需要改變上面http請求的key值,再向豌豆莢的服務器發送請求即可。
請求url
在python中可以使用urllib、request庫等。
提取數據
發送http請求后,豌豆莢服務器會返回搜索的app列表。可以看到是一個html文檔。用Chrome瀏覽器,查看返回的html文檔,定位到相關的信息。如下圖,可以看到,微信的圖標的鏈接地址。這就是我們需要提取的數據。
在python中我們可以使用正則表達式來提取圖標的鏈接地址,或者對于html可以使用BeautifulSoup來提取。
保存數據
上面提取的是圖標的鏈接地址,我們可以再次用這個鏈接地址發起http請求,就可以得到相應圖標的數據,然后把數據保存到磁盤中即可。
代碼如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""從豌豆莢應用市場抓取app列表的圖標"""
from urllib.parse import quote
from urllib import request
from functools import wraps
import re
__author__ = 'heql'
def url_decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(*args)
if args:
return func(*args)
return wrapper
@url_decorate
def request_url(url):
"""請求url"""
cnt = 3
for i in range(cnt):
try:
with request.urlopen(url, timeout=20) as f:
return f.read()
except Exception as e:
if(i < cnt - 1):
print('request again')
else:
print('except:', e)
def save_icon(file_name, data):
with open(file_name, 'wb') as f:
f.write(data)
def get_icon_links(data, app_name, compare=None):
"""提取圖標的鏈接"""
items = re.findall(r'(<div class="icon-wrap">(?:.|\n)*?</h2>)', data)
for item in items:
icon_link = re.search(r'<img src="(.*?)"', item).group(1)
icon_app_name = re.search(r'class="name">(.*?)</a>', item).group(1)
result = True if compare is None else compare(icon_app_name, app_name)
if result:
yield icon_app_name, icon_link
def read_app_list(file_name):
"""讀取app列表"""
with open(file_name, encoding='utf-8') as f:
return [line.strip() for line in f if line.strip()]
def main():
app_name_list = read_app_list('app_list.txt')
url = 'http://www.wandoujia.com/search?key={0}&source=index'
for app_name in app_name_list:
data = request_url(url.format(quote(app_name, encoding='utf-8')))
if not data:
continue
for index, (icon_app_name, icon_link) in enumerate(get_icon_links(data.decode('utf-8'), app_name, lambda left, right: left == right)):
icon_data = request_url(icon_link)
if not icon_data:
continue
file_name = '{0}_{1}.png'.format(icon_app_name, index)
save_icon(file_name, icon_data)
if __name__ == '__main__':
main()
代碼分析:
read_app_list函數: 從文件中讀出app列表的名稱,每一行對應一個app名稱。
request_url函數: 使用urllib發送http請求。如果請求時發生異常(如:網絡超時、url請求錯誤等),則再次發送請求,如果同一個url超過三次請求有誤,則返回None。它有一個裝飾器url_decorate,用于輸出發送請求時的url。
get_icon_links函數: 用于提取服務器返回的數據。使用正則表達式findall匹配所有符合以<div class="icon-wrap">開始,以/h2>結束的選項。findall會返回一個list。然后對每個選項用整個表達式提取圖標的鏈接和圖標的名稱。compare 是一個回調函數,當為None時表示要提取所有的圖標信息,代碼傳入一個lambda表達式,lambda left, right: left==right表示只有提取的圖標名稱和搜索的圖標的名稱相同時,才返回其對應的圖標鏈接。
save_icon函數: 寫入圖標的數據到指定的文件中。圖標保存的名稱是以名字_下標形式保存的,因為有可能會出現兩個以上名字相同的數據,比如說:搜索可可英語。會有兩個相同的app名稱的應用。
使用BeautifulSoup提取html數據
Beautiful Soup 是一個可以從HTML或XML文件中提取數據的Python庫。
參考: https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
使用BeautifulSoup后能讓代碼更加簡潔,對于上面的程序,我們只需修改get_icon_links即可。代碼如下:
# 提取圖標的鏈接
def get_icon_links(data, app_name, compare=None):
soup = BeautifulSoup(data, 'lxml')
for tag in soup.find_all('img'):
icon_link = tag['src']
icon_app_name = tag['alt']
result = True if compare is None else compare(icon_app_name, app_name)
if result:
yield icon_app_name, icon_link