顯示具有 Python Crawler 標籤的文章。 顯示所有文章
顯示具有 Python Crawler 標籤的文章。 顯示所有文章

2017年6月13日 星期二

Python邊學邊記錄-Crawler網路爬蟲-實戰-虎航2_selenium

利用selenium來處理的話,就不需要再去看data帶了什麼資料了!
selenium本身會直接操作browser,所以頁面上的欄位也要直接的給值,而不是去透過data給值了!

首先,先設定好webdriver!
chrome_path = 'D:\pyCrawler\selenium_driver_chrome\chromedriver.exe'driver = webdriver.Chrome(chrome_path)
driver.maximize_window()
driver.set_page_load_timeout(60)
driver.get(TigerUrl)

然後,就要開始找畫面上的欄位定位了!

來回:應該是不用去調整才對!
起發:ControlGroupSearchView_AvailabilitySearchInputSearchVieworiginStation1
抵達:ControlGroupSearchView_AvailabilitySearchInputSearchViewdestinationStation1
去程日:ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketDay1
去程年月:ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketMonth1
回程日:ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketDay2
回程年月:ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketMonth2
成人:ControlGroupSearchView$AvailabilitySearchInputSearchView$DropDownListPassengerType_ADT
兒童:ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListPassengerType_CHD
嬰兒:ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListPassengerType_INFANT
獲取航班:ControlGroupSearchView_ButtonSubmit

element = WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located((By.ID,
 'ControlGroupSearchView_ButtonSubmit')))
#  下拉選單需要拆段作業,先定位點擊,然後再巡覽選項#  出發機場el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchVieworiginStation1'))
el.select_by_value('TPE')
#  抵達機場el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchViewdestinationStation1'))
el.select_by_value('DMK')
#  去程日el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketDay1'))
el.select_by_value('21')
#  去程年月el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketMonth1'))
el.select_by_value('2017-06')
#  回程日el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketDay2'))
el.select_by_value('30')
#  回程年月el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListMarketMonth2'))
el.select_by_value('2017-06')
#  成人數el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListPassengerType_ADT'))
el.select_by_value('3')
#  兒童數el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListPassengerType_CHD'))
el.select_by_value('0')
#  嬰兒數el = Select(driver.find_element_by_id(
'ControlGroupSearchView_AvailabilitySearchInputSearchView_DropDownListPassengerType_INFANT'))
el.select_by_value('0')
#  按下獲取航班driver.find_element_by_id(
'ControlGroupSearchView_ButtonSubmit').click()
#  透過等待的設定來待網頁,看到需求的元件就往下執行了!WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located((By.ID, 'flightSpinner')))

條件的部份後續可以再優化,透過文字檔來處理!
不過基本上這樣子已經可以取得資料了!
# 透過page_source回傳網頁文件給BeautifulSoup處理
soup = BeautifulSoup(driver.page_source, 'html5lib')
tbs = soup.select('.select-flight')

start_lightPrice = soup.select('#tableMarket1 > tr > td')[1].select('label > span')
print('sl:', start_lightPrice)
start_comboPrice = soup.select('#tableMarket1 > tr > td')[2].select('label > span')
print('sc:', start_comboPrice)
end_lightPrice = soup.select('#tableMarket2 > tr > td')[1].select('label > span')
print('el:', end_lightPrice)
end_comboPrice = soup.select('#tableMarket2 > tr > td')[2].select('label > span')
print('ec:', end_comboPrice)

BeautifulSoup也有CSS選擇器,不過在td的部份,一直想透過td.light price來做定位一直無法成功,如果有路過的前輩知道也請指導!

再來處理一下,就可以弄成給預算還有旅遊區間,然後讓程式自動去爬,一但有了就自動發出mail來通知你有便宜的機票了!

不過現在一堆機票搜尋都寫那麼好了....=..=





2017年6月12日 星期一

Python邊學邊記錄-Crawler網路爬蟲-實戰-虎航

Python Crawler

在爬虎航的航班資訊的時候,好像不是這麼標準SOP就可以取得網頁資料了!

透過開發者工具可以發現,這個SelectFlights.aspx是有來源網址的,這代表是在一個地方先搜尋之後再到這來。
Python Crawler

再看一下Search.aspx的部份

Python Crawler

Python Crawler

另外也可以看一下Search.aspx的response會發現是空的!
這代表在這邊是沒有回傳資料的,而SelectFlights.aspx的response是有的!
Python Crawler

Python Crawler

所以,程式的部份就需要改透過request.session來處理!
resp = requests.session()

resp1 = resp.post('https://booking.tigerairtw.com/Search.aspx', data=form_data)
resp2 = resp.get('https://booking.tigerairtw.com/SelectFlights.aspx')

soup = BeautifulSoup(resp2, 'html5lib')

這樣子,就可以取得航班資訊了!
測試的時候更新太多次,被ban了...技術不好就是一下子會被發現是爬蟲…改用selenium了!

2017年6月8日 星期四

Python邊學邊記錄-Crawler網路爬蟲-第八課-創建目錄與下載圖片

Python Crawler

在爬文章的過程中,我們會遇到想要的圖片要下載下來,像是捷克漫畫版如果有新的連載上來了,一定會癢癢的,或是表特版的正妹也是。

這時候要處理的還是怎麼去判斷這個連結是不是我們要的連結,就是又要靠正則式了!

基本上,主程式碼的部份都跟之前的相去不遠,只是多了幾個function處理新增的功能。

取得所有文章列表之後開始做後面的工作
for article in articles:
    page = get_articles(ptt_url + article['href'])
    if page:
        img_urls = parse(page)  #  這邊主要要取得圖片的連結
        saveImage(img_urls, article['title'])  #  這邊要保存圖片

接著要處理文章內的圖片連結(這個要視圖床,較多人在ptt分享都是透過imgur.com。
def parse(dom):
    soup = BeautifulSoup(dom, 'html.parser')
    links = soup.find(id='main-conetent').find_all('a')
    imgurls = []
    for link in links:
        if re.match(r'^https?://(i.)?(m.)?imgur.com',link['href']):
            imgurls.append(link['href'])
    return imgurls

最後就是把連結的資料拉下來了!
def saveImage(img_urls, title):
    if img_urls:
        try:
            dname = title.replace('?', '').replace('', '').replace(' ', '') 
            .replace('Re:', '').strip()  #  strip() 去除字串前後的空白            os.makedirs(dname)  #  創建資料夾
            for img_url in img_urls:  #  主要是把下載連結的格式再調整
                if img_url.split('//')[1].startswith('m.'):
                    img_url = img_url.replace('//m.', '//i.')
                if not img_url.split('//')[1].startswith('i.'):
                    img_url = img_url.split('//')[0] + '//i.' + img_url.split('//')[1]
                if not img_url.endswith('.jpg'):
                    img_url += '.jpg'                fname = img_url.split('/')[-1]
                urllib.request.urlretrieve(img_url, os.path.join(dname, fname))
        except Exception as e:
            print(e)

這邊記得要import os與urllib.request

Python邊學邊記錄-Crawler網路爬蟲-第七課-取得IP國家

Python Crawler

在第六課的時候,我們已經成功的取得了PTT的文章了,但是這樣還不夠,如果可以的話,我們還想取得這發文IP來源是那一個國家,或者是記下這個IP來查詢,到底那幾個帳號是從同一個IP出來就可以分出這是不是住一起的,或是分身帳號!

如果要查詢國家的話,可以透過http://freegeoip.net/json/網址或是ip來做查詢!
這是一個免費查詢ip國家的服務,每小時可以有15000次的查詢額度。

freegeoip回傳json格式如下:
freegeoip


我們在取得文章列表之後,寫入了articles這個list內,
country_to_count = dict()
for article in articles:
    print('文章IP:', article['title'])
    page = get_articles(ptt_url + article['href'])
    if page:
        ip = get_ip(page)
        country = get_country(ip)
        if country in country_to_count.keys():
            country_to_count[country] += 1        else:
            country_to_count[country] = 1

單純的迴圈計算出現在touple內出現的次數,若是沒有出現就給值『1』

get_ip的部份如下:
def get_ip(SoureceIP):
    reIP = '來自: \d+\.\d+.\d+.\d+'  #  這是把頁面內的發文ip取出的正則式
    match = re.search(reIP,SourceIP)  #  確認文章內是否有相對應的格式
    if match:
        return match.group(0).replace('來自: ','')  #  回傳第一個,另外也取代掉『來自: 』
    else:
        return None

取回ip之後,就丟給freegeoip去回傳json
def get_country(ip):
    if(ip):
        data = json.loads(request.get('http://freegeoip.net/json/' + ip).text)
        countryName = data['country_name'] if data['country_name'] else None
        return countryName 
    return None

因為有用到json.loads,記得要import json!
大概就是這樣了。









2017年6月1日 星期四

Python邊學邊記錄-Crawler網路爬蟲-第六課-PTT文章爬取

Python Crawler

ptt的sex版,每日每夜都有廢文產生,有時候沒有跟到的話,就只能500P求圖!
這時候就可以放著自動去爬了..

透過網頁版去登入的時候會發現,需要滿18才可以進去!
Python Crawler

這時候可以透過開發工具發現,client端送了一個cookie『over18=1』給了server!
Python Crawler

所以我們就可以利用這點來讓server相信,我們滿18了!
我們在透過requests.get的時候,就可以送個訊息給server端了:
resp = requests.get(
    url=url,    cookies={'over18': '1'}
)

再來就是要先確認,我們需要那些資訊!
Python Crawler

流程的部份:
我們登入首頁(此時是最新文章)->記錄上頁連結->確認有無本日文章->回到上頁->確認有無本日文章...如此迴圈!
PYTHON crawler
需求套件:
import requests
import time
import json
from bs4 import BeautifulSoup

公用變數:
ptt_url = 'https://www.ptt.cc' # 保留以後可以變數帶入其它板的機會

主程式的部份:

def __name__ == '__main__':
    connect_page =  checkStatus(ptt_url + '/bbs/sex/index.html') #確認網頁狀態是否正常
if connect_page:
        articles = [] #用來記錄文章list
        today = time.strftime("%m/%d").lstrip('0') #今天日期格式調整,time是import的模組
        today_articles,pre_url = get_articles(connect_page,today) #呼叫爬蟲fucntion
        # 到上面,已經可以爬到主頁上的文章了!
        # 爬完了之後,要進入驗證的迴圈,確認有無其它本日文章!
        while today_articles: #確認有無本日文章,如果list回來已經沒有東西了,那就代表沒有了!
            articles += today_articles #把爬回傳的list先寫丟進去主list
            connect_page = checkStatus(pre_url) #一樣需要做網頁狀態驗證
            today_articles,pre_url = get_articles(connect_page,today) #再呼叫爬蟲!







checkStatus(url):
# 主要確認網頁是否正常
def checkStatus(url):
    resp = requests.get(
        url = url,
        cookies = {'over18':'1'}
    )
    if resp.status_code != 200: #如果網頁狀態不存活了,就直接return None
        return None
    else:
        return resp.text

get_articles():
# 資料爬取的主要程式
def get_articles(respText,date): #一個是resp拋過來的資料,一個是今日的日期。
    soup = BeautifulSoup(respText,'html5lib')
    #取得網頁的上頁連結
    pre_div = soup.find('div','btn-group btn-group-paging')
    pre_url = pre_div.find_all('a')[1].href
    
    articles = [] #用來記錄文章list
    divs = soup.find_all('div','r-ent') #ptt的文章,都放在class是r-ent上!
    for d in divs:
        if d.find('div','date').text.strip() == date: #確認日期跟今天的日期是否相同
            #取得推文數
            pushCount = 0
            pushStr = d.find('div','nrec').text
            #因為推文有時候會是『X』被噓爆,或是『爆』被推爆,所以要特別處理。
            if pushStr:
                try:
                    pushCount = int(pushStr) #字串轉數字
                except ValueError:
                    if pushStr =="爆":
                        pushCount=99
                    elif pushStr.startswith('X'):
                        pushCount=-10

            #取得文章連結跟標題
            #這邊可以依需求自取,甚至可以再另外寫一個function去爬發文者的ip來做記錄!
            #後面可以利用那發文ip來比對id,就知道誰有什麼分身了...
            if d.find('a'): #有超連結就代表文章還活著,沒有被d掉。
                href = d.find('a').href #連結
                articleTitle = d.find('a').text #標題
                author = ''
                articles.append({
                    'title':articleTitle,
                    'href':href,
                    'author':author,
                    'pushCount':pushCount
                })
    return articles,pre_url

以上!
後續再來加追去取得發文者ip!



2017年5月25日 星期四

Python邊學邊記錄-Crawler網路爬蟲-第四課-爬表格

Python Crawler

今天的課是學怎麼去爬表格的資料,作法上跟之前在寫ASP.NET的時候處理GridView差不多,果然是萬變不離其宗!

假如網頁畫面如下:
項次 項目 價格 連結
1 國文 1200 http://123.com
2 英文 1800 http://123.com
3 數學 1500 http://123.com
4 理化 2000 http://123.com

首先,一樣要先透過requests.get連到該目標網址,然後一樣丟給了BeauitfulSoup去處理!

resp = requests.get('目標網址')
soup = BeautifulSoup(resp.text, 'html.parser')

tr就跟row一樣,所以先取tr資料
rows = soup.find('table', 'table').tbody.find_all('tr')

然後就透過迴圈去把所有tr的價格資料取出,價格td在第三欄,以index來計算的話是2。
(註:目前只有遇到generol的index是從1開始@@)

for row in rows:
  price = row.find_all('td')[2].text  

基本上,這樣子就可以取得price了。

如果有想要換平均課程價格的話,那就可以先宣告一個list
prices = []

然後在迴圈中append進去
for row in rows:
  price = row.find_all('td')[2].text
  prices.append(int(price))

總金額
sum(prices)
len(prices)
課程數

python的list加總真的很方便!

另一種作法的話,就是透過tag的父子兄關係去做定位。
table
  tr
    td
    td
    td價格
    td連結
       a

我們可以從『a』這個tag去找他爸『td連結』再找他兄弟『td價格』
這時候的作法就變成先取得『a』的定位
links = soup.find_all('a')
接著透過『a』來找他的父兄
for link in links:
  price=link.parent.previous_sibling.text

.parent(父).previous_sibling(兄) 作法上跟處理一些網頁是一樣的。

如果要把所有的表格資料列印出來的話,作法是一樣的。
rows = soup.find('table','table').find_all('tr') # 先取得所有的tr資料
for row in rows:
  #另一種取得所有td的方式 
  #all_tds = [td for td in row.children]
  all_tds = row.find_all('td') # 取得所有的td
  print(all_tds[0].text..XXXX) # 透過index去取值即可

當然了,如果有時候連結沒有放上去的話,那就會造成異常,所以需要防呆!
rows = soup.find('table','table').find_all('tr') # 先取得所有的tr資料
for row in rows:
  all_tds = row.find_all('td') # 取得所有的td
  if 'href' in all_tds[3].a.attrs: # 確認href是否存在
    href = all_tds[3].a['href']
  else:
    href = None
  print(all_tds[0].text..XXXX) # 透過index去取值即可

另一種作法的話就是可以透過stripped_strings來處理!
rows = soup.find('table','table').find_all('tr') # 先取得所有的tr資料
for row in rows:
  print([s for s in row.stripped_strings])

s for s in subsets 就等於
ss = []
for s in subsets(s):
  ss.apped(s)

2017年5月24日 星期三

Python邊學邊記錄-Crawler網路爬蟲-第二課-防呆

Python Crawler

Python邊學邊記錄-Crawler網路爬蟲-第一課
已經可以取得網頁的資料了,距離目標也就進了一步!
但是我們會發現,如果今天是一個網頁那就沒事,起爬了就開始走,但是如果網頁不存在呢?

Python Crawler

程式就中斷了...如果只是一個網頁是小事,如果有很多網頁的話,第一個網頁就掛了,那這中間的等待時間都白費了,所以我們需要去做例外處理!!

try:
    resp = requests.get('http://marty.blogspot.com/p/python.html')
except:
    resp = None
if rsep and resp.status_code == 200 :
    soup = BeautifulSoup(resp.text, 'html.parser')
    print(soup.find('h1').text)

透過try except來讓程式可以順利的執行完畢。

Python Crawler

如此程式就會順利的一直走下去,不會是上面的錯誤中斷了。
但是,如果還有其它的呢?像tag不在存之類的...總不能...遇見一個就try一個吧!

所以我們就可以把程式碼重構一下,把判斷的部份另外拉出一個fucntion

def get_tag(url, head_tag):
    try:
        resp = requests.get(url)
        if resp.status_code == 200:
            soup = BeautifulSoup(resp.text, 'html.parser')
            return soup.find(head_tag).text
    except Exception as e:
        return None

這樣子,就可以美觀又防呆了!









2017年5月23日 星期二

Python邊學邊記錄-Crawler網路爬蟲-第一課-開始爬

Python Crawler需求套件:


  • BeautifulSoup
  • Requests
首先,要先import requests跟BeautifulSoup

Requests是一個在網路資源取得的套件,可以get、post、delete!
我們要從網站取得資料的時候可以透過requests.get('網址')來操作執行!

resp = requests.get('http://martychen920.blogspot.com/p/python.html')

python Requests

這時候,resp取得資料之後,其實有很多的操作方法,像status確認網頁狀態,這邊我們要將網頁資料整個拉出的話,就是text!
所以,可以用print(resp.text)去看,會發現整個html都被搬過來了。

python Requests

接著,這html的資料還要再過手,轉成BeautifulSoup看的懂的格式!

soup = BeautifulSoup(resp.text,'html.parser')

這樣,就可以把資料轉成BeautifulSoup這套件自己可以懂的格式了。
這時候去print(soup),也會是一堆像極了html的資料。
接著就可以去操作這soup上的資料了!

soup.find('h1').text

這樣就可以去找尋『h1』並取得文字資料。
假設是『藤原栗子工作室

如果直接去print(soup.find('h1'))的話也是可以執行的,只是會連tag都帶出來而以。
就會是『<h1>藤原栗子工作室</h1>