【CTF】廣度搜索的 BeautifulSoup 網站爬蟲

本人習慣使用python2進行編程,所以beautifulsoup也是使用python2版本的,但聽說python2明年就要中止支持了,憂傷得很。。。

0x01 題目要求

如圖所示,在頁面源碼當中找到5個flag,而後拼接起來,還給了flagA的示例。html

圖片描述

flagA:python

圖片描述

打開站點是一個ctf-wiki的demo站點,瞭解這個站的人應該都知道它的體量,因此手動一個個找是不現實的,須要用到爬蟲了(題目名稱也暗示了)。編程

0x02 解題思路

我考慮使用廣度優先搜索(BFS)實現一個網站爬蟲,不瞭解廣度搜索的童鞋能夠自行百度。具體實現方法以下:segmentfault

  1. 創建待請求連接visiting_urls和已請求連接visited_urls的2個列表(也可看作隊列)
  2. visiting_urls取出一條連接,使用requrests.get請求頁面源碼
  3. 在源碼中正則匹配flag字段
  4. beautifulsoup獲取頁面中全部的a標籤,符合要求的加入visiting_urls
  5. visiting_urls不爲空,則執行[2]

當中須要考慮2個問題:session

  1. 去重問題:當爬取連接時,不免會遇到存在不一樣位置的url指向同一個頁面,爬取時不須要再請求相同頁面,所以要對爬取到的url進行去重。方法以下:app

    • 維護visiting_urls visited_urls列表,比對爬取url與已爬取過的url是否重複;
    • 根據mkdocs網站url特色,包含"../"的是回溯連接,此類連接不須要再次請求。
  2. 正則匹配問題:這個方面沒有多想,寫個能使用的正則匹配規則就行,在本題中須要2種正則匹配:優化

    • 匹配flag:flag[ABCDE],個人目的是匹配到flag的標誌,而不是把flag整個都匹配出來,由於我不清楚flag當中有沒有其餘奇怪字符,防止出現漏匹配的狀況;
    • 匹配url:[\w\/]+index\.html,目的是匹配路徑爲字母數字(不包含"..")且末尾是"index.html"url

到此,整個任務就完成了。網站

0x03 完整腳本

#coding=utf-8
import requests,re
from bs4 import BeautifulSoup

s = requests.session()
s.keep_alive=False

flagre = re.compile('flag[ABCDE]')
urlre = re.compile('[\w\/]+index\.html')

base_url = 'http://23.236.125.55:1000/ctf-wiki/'
flagA_url = 'http://23.236.125.55:1000/ctf-wiki/assembly/mips/readme/index.html'

visiting_urls = ['http://23.236.125.55:1000/ctf-wiki/index.html']
visited_urls = []

def find_flag(url,html):
    flist = flagre.findall(html)
    if len(flist) > 0:
        print flist,url

def BFS():
    url = visiting_urls[0]
    del(visiting_urls[0])
    visited_urls.append(url)
    r = s.get(url)
    #r.encoding = 'utf-8'
    find_flag(url,r.text)
    soup = BeautifulSoup(r.text,'lxml')
    for a in soup.find_all('a'):
        link = a['href']
        if urlre.findall(link) and ".." not in link:
            new_url = base_url + link
            if new_url not in visited_urls and new_url not in visiting_urls:
                visiting_urls.append(new_url)

if __name__ == '__main__':
    while len(visiting_urls) > 0:
        BFS()

上面思路已經提到了,該腳本只能提取到包含flag標誌的頁面,而不是flag自己,所以還須要手動訪問這些頁面去尋找flag(手動狗頭),若是還想直接顯示flag,那就須要優化一下正則匹配了。url

提示一點,在獲取到頁面源碼後,使用r.encoding = 'utf-8'轉碼會致使EOFError,具體緣由不詳,本想可以匹配中文頁面,結果多此一舉搞了半天覺得匹配沒成功。spa

2019.05.05補充:在爬取含中文的utf-8頁面時,使用print輸出Response字符串在各類狀況下顯示狀況:(以匹配title標籤爲例)

語言 輸出 cmd/PowerShell Linux Shell
py2 r.text
<'unicode'>
會報錯UnicodeEncodeError: 'gbk' codec can't encode character u'\u2f8f' in position 96807: illegal multibyte sequence u'<title>Web \u5e94\u7528\u7b80\u4ecb - CTF Wiki</title>'
py2 r.content
<'str'>
'<title>Web \xe5\xba\x94\xe7\x94\xa8\xe7\xae\x80\xe4\xbb\x8b - CTF Wiki</title>'
並會報錯IOError: [Errno 34] Result too large
'<title>Web \xe5\xba\x94\xe7\x94\xa8\xe7\xae\x80\xe4\xbb\x8b - CTF Wiki</title>'
py3 r.text
<'str'>
'<title>Web 應用簡介 - CTF Wiki</title>' '<title>Web 應用簡介 - CTF Wiki</title>'
py3 r.content
<'bytes'>
b'<title>Web \xe5\xba\x94\xe7\x94\xa8\xe7\xae\x80\xe4\xbb\x8b - CTF Wiki</title>'
正則匹配會報錯TypeError: cannot use a string pattern on a bytes-like object
b'<title>Web \xe5\xba\x94\xe7\x94\xa8\xe7\xae\x80\xe4\xbb\x8b - CTF Wiki</title>'
正則匹配會報錯TypeError: cannot use a string pattern on a bytes-like object

由上述表格可見,若是涉及到中文頁面的爬蟲程序,儘可能使用python3&Linux Shell運行,而且合理使用r.contentr.textr.content.decode('utf-8')==r.text)。

提示兩點,requests.session()的好處,相較於直接requests.get(),能夠防止創建過多的HTTP鏈接,致使新鏈接沒法創建的問題。參考頁面:https://segmentfault.com/q/10...

執行效果以下:

圖片描述

最後拼接一下,完事了。

相關文章
相關標籤/搜索