本人习惯使用python2进行编程,因此beautifulsoup也是使用python2版本的,但据说python2明年就要停止支持了,忧伤得很。。。
0x01 题目要求
如图所示,在页面源码当中找到5个flag,然后拼接起来,还给了flagA的示例。
flagA:
打开站点是一个ctf-wiki
的demo站点,了解这个站的人应该都知道它的体量,所以手动一个个找是不现实的,需要用到爬虫了(题目名称也暗示了)。
0x02 解题思路
我考虑使用广度优先搜索(BFS)实现一个网站爬虫,不了解广度搜索的童鞋可以自行百度。具体实现方法如下:
- 建立待请求链接
visiting_urls
和已请求链接visited_urls
的2个列表(也可看做队列) - 从
visiting_urls
取出一条链接,使用requrests.get
请求页面源码 - 在源码中正则匹配flag字段
-
beautifulsoup
获取页面中所有的a
标签,符合要求的加入visiting_urls
-
visiting_urls
不为空,则执行[2]
当中需要考虑2个问题:
-
去重问题:当爬取链接时,难免会遇到存在不同位置的
url
指向同一个页面,爬取时不需要再请求相同页面,因此要对爬取到的url
进行去重。方法如下:- 维护
visiting_urls visited_urls
列表,比对爬取url
与已爬取过的url
是否重复; - 根据mkdocs网站
url
特点,包含"../"
的是回溯链接,此类链接不需要再次请求。
- 维护
-
正则匹配问题:这个方面没有多想,写个能使用的正则匹配规则就行,在本题中需要2种正则匹配:
- 匹配flag:
flag[ABCDE]
,我的目的是匹配到flag的标志,而不是把flag整个都匹配出来,因为我不清楚flag当中有没有其他奇怪字符,防止出现漏匹配的情况; - 匹配url:
[\w\/]+index\.html
,目的是匹配路径为字母数字(不包含".."
)且末尾是"index.html"
的url
。
- 匹配flag:
到此,整个任务就完成了。
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,那就需要优化一下正则匹配了。
提示一点,在获取到页面源码后,使用
r.encoding = 'utf-8'
转码会导致EOFError
,具体原因不详,本想能够匹配中文页面,结果画蛇添足搞了半天以为匹配没成功。
2019.05.05
补充:在爬取含中文的utf-8页面时,使用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.content
和r.text
(r.content.decode('utf-8')==r.text
)。提示两点,
requests.session()
的好处,相较于直接requests.get()
,可以防止建立过多的HTTP连接,导致新连接无法建立的问题。参考页面:https://segmentfault.com/q/10...
执行效果如下:
最后拼接一下,完事了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。