爬取锦城学院图书馆学生借书信息
http://jccxxt.scujcc.cn/ReaderLogin.aspx
这个网站爬了我接近三个星期,对,你没听错,先贴上我一直错的code
1 import requests 2 from bs4 import BeautifulSoup 3 import time 4 import traceback 5 6 def Get__EVENTVALIDATION(Soup): 7 res = Soup.find(attrs = {'name':'__EVENTVALIDATION'}) 8 return res['value'] 9 10 def Get__VIEWSTATE(Soup): 11 res = Soup.find(attrs = {'name':'__VIEWSTATE'}) 12 return res['value'] 13 14 # def Get__EVENTTARGET(Soup): 15 # res = Soup.find(attrs = {'name':'__EVENTTARGET'}) 16 # return res['value'] 17 18 # def Get__EVENTARGUMENT(Soup): 19 # res = Soup.find(attrs = {'name':'__EVENTARGUMENT'}) 20 # return res['value'] 21 22 def login(url, Soup, Sno, session): 23 try: 24 info = { 25 # "ScriptManager1":"UpdatePanel1|ImageButton1", 26 "__EVENTTARGET":'', 27 "__EVENTARGUMENT":'', 28 "__VIEWSTATE":Get__VIEWSTATE(Soup), 29 "__EVENTVALIDATION":Get__EVENTVALIDATION(Soup), 30 # "DropDownList1":"借书证号", 31 "TextBox1":Sno, 32 # "TextBox2":"", 33 # "__ASYNCPOST":"true", 34 # "ImageButton1.x":"1", 35 # "ImageButton1.y":"1" 36 } 37 header = { 38 # 'User-Agent':'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', 39 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36', 40 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 41 'Accept-Encoding':'gzip, deflate', 42 'Accept-Language':'zh-CN,zh;q=0.9', 43 'Referer':'http://jccxxt.scujcc.cn/ReaderLogin.aspx', 44 'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8', 45 # 'Cookie':'UM_distinctid=162f7c3dcc4420-07d6206f36b7a6-b353461-100200-162f7c3dcc56a; yunsuo_session_verify=aa35d988c77a05529e4b35a753866ad6; ASP.NET_SessionId=xo5txp3z2xtn2n55xhza2ouz', 46 'Host':'jccxxt.scujcc.cn' 47 # 'Proxy-Connection':'keep-alive' 48 } 49 r = session.post(url, data = info, headers = header) 50 time.sleep(3) 51 return r.text 52 except: 53 traceback.print_exc() 54 return print('post不成功') 55 56 def main(): 57 with requests.Session() as session: 58 url = "http://jccxxt.scujcc.cn/ReaderLogin.aspx" 59 html = session.get(url).text 60 Soup = BeautifulSoup(html, 'html.parser') 61 html_login = login(url, Soup, "163020397", session) 62 Soup_login = BeautifulSoup(html_login, 'html.parser') 63 64 print(Soup_login.prettify()) 65 if Soup_login.find('span', attrs = {'class':'TableContent1'}) is not None: 66 print('Success') 67 else: 68 print('Fail') 69 70 main()
解释下我的心路历程:
先开始爬这个网站的时候,什么都不知道,以为提交一个用户名(name='TextBox1')就行了,后来发现自己太天真(ps:有很多人是没有密码的)
这个网站居然隐藏了4个hidden字段的表单,你每刷新一次页面,这些field(字段)是会变的
所以我们要使用会话Session,Session详见我的blog:http://www.cnblogs.com/ducklu/p/9026743.html
回了Session以后,我发现还是不行
我又去不断研究,后来知道提交表单的时候有两种情况:
1.所有你知道的表单项都要提交
2.蜜罐!!!就是有些其实是不用提交的,这个网站就看你提交这个字段没有,如果提交了,他就知道你不是“人”了!!!
然后我就得到了,最后这段code,我不断更改里面的字段值,发现还是不行,这时我又懵逼了
然后,我又去研究,学到了一个新知识,selenium,哇,这个真的是神器(我可以直接通过浏览器模拟登陆),我在之后会对官方文档写一篇详细的解读的blog
于是,又在一番折磨下,我终于成功了!!!贴上代码,里面有我的详细注释
1 import sqlite3 2 import time 3 import unittest 4 from selenium import webdriver 5 from selenium.webdriver.common.keys import Keys 6 from selenium.webdriver.support.ui import Select 7 8 # con = sqlite3.connect('jcLib.sqlite') 9 # con.close() 10 class JCLibScrapy(unittest.TestCase): 11 12 def setUp(self): 13 self.con = sqlite3.connect('jcLib.sqlite') 14 15 # 每个测试方法均以 test 开头,否则是不被unittest识别的。 16 def test_running(self): 17 driver = webdriver.Chrome('E:/chromedriver.exe') 18 for i in range(1, 450): 19 if (int(i / 100)): 20 Sno = '163020' + str(i) 21 elif (int(i / 10)): 22 Sno = '1630200' + str(i) 23 else: 24 Sno = '16302000' + str(i) 25 26 driver.get("http://jccxxt.scujcc.cn/ReaderLogin.aspx") 27 elem = driver.find_element_by_name('TextBox1') 28 elem.clear() 29 elem.send_keys(Sno) 30 time.sleep(3) 31 elem.send_keys(Keys.RETURN) 32 time.sleep(3) 33 # 访问太快了得不到想要的page,必须要停滞一会 34 # print(driver.page_source) 35 36 # 存储结果 37 self.res = {} 38 #存储借的books 39 books = [] 40 41 # 已经进入了我们要爬取的页面,我们只需要获得想要的info即可 42 # 不知道tr有多少,所以用异常处理遇到没有的tr跳出 43 for i in range(2, 100): 44 try: 45 # 这里有个很重要的点,我在阅读了官方文档后并没有发现,那就是这里只能找elements!!!,不能直接找他的属性,因为我们用的是find_element 46 # 我先开始的错误写法 '//*[@id="DataGrid1"]/tbody/tr[' + str(i) + ']/td[2]/text()' 47 # 因为不能直接获取属性,我们只能获取Elment(学过js都知道,getElementById!!!) 48 elem_name = driver.find_element_by_xpath('//*[@id="LblreaderName"]') 49 # 获取属性用这个 50 # res = elem.get_attribute('id') 51 self.res['name'] = elem_name.text 52 53 elem_books = driver.find_element_by_xpath('//*[@id="DataGrid1"]/tbody/tr[' + str(i) + ']/td[2]') 54 book = elem_books.text 55 books.append(book) 56 # print(elem) 57 except: 58 # 说明超出范围了,同时也说明获取完了,所以这个时候更新res并且入库 59 if self.res: 60 self.res['book'] = books 61 self.res['Sno'] = Sno 62 self.InDB() 63 # 如果登录不成功,则重启浏览器,并且break,不入库 64 else: 65 driver.close() 66 driver = webdriver.Chrome('E:/chromedriver.exe') 67 break 68 driver.close() 69 70 # 把res入库 71 def InDB(self): 72 cu = self.con.cursor() 73 insert_sql = 'insert into SBook (name, book, Sno) values ("{}", "{}", "{}")'.format(self.res['name'], self.res['book'], self.res['Sno']) 74 cu.execute(insert_sql) 75 self.con.commit() 76 # 记得字典要清空,要不无法判断登录是否成功 77 res = {} 78 79 def tearDown(self): 80 self.con.close() 81 82 if __name__ == '__main__': 83 unittest.main()
附上我的数据库信息
然后你会发现,有很多人是设有密码的,这个时候我就想,枚举不就行了吗,后来我想了一下时间复杂度,哇,是n的n次方量级的啊,算到天昏地暗都算不出来(除非它密码短)
所以,现在,我准备研究一个破解密码的算法,此篇blog完结
不忘初心,方得始终