【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...

执行效果以下:

图片描述

最后拼接一下,完事了。

相关文章
相关标签/搜索