在正式爬取之前,先做一個試驗,看一下爬取的數據對象的類型是如何轉換為列表的:
寫一個html文檔:
x.html
<html><head><title>This is a python demo page</title></head> <body> <p class="title"> <a>The demo python introduces several python courses.</a> <a href="http://www.icourse163.org/course/BIT-133" class="py1" id="link1">Basic Python</a> </p> <p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: <a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a> </p> </body></html>
# coding:utf-8 from bs4 import BeautifulSoup import requests import bs4 soup = BeautifulSoup(open('D:/x.html', encoding='utf-8'), "html.parser") print(soup.find('body',).children) # .children返回可迭代對象,不是列表,需要用for循環遍歷其中的內容 for t in soup.find('body').children: 迭代<body>標簽的兒子節點 if isinstance(t, bs4.element.Tag): # 判斷子節點是否為Tag對象(因為子節點會包含如換行符之類的節點) print('body的子標簽的內容是:', t) # 查看t變量獲得的對象內容,body的子標簽為p標簽,一組<p></p>表示一個對象 print('t的類型是:', type(t)) # 查看t的類型
可以看到每個t對象的類型是bs4.element.Tag,也就是標簽對象。
那么,如果要從每個t對象中獲取a標簽的內容,並把所有a標簽都保存到一個列表中,該如何做?
可以使用:
list = t('a') # t('a')會生成一個bs4.element.ResultSet類型的數據對象,實際上就是Tag列表
for t in soup.find('body').children: if isinstance(t, bs4.element.Tag): # 判斷子標簽是否為Tag對象(因為子節點會包含如換行符之類的節點) # print('body的子標簽的內容是:', t) # 查看t變量獲得的對象內容 # print('t的類型是:', type(t)) # 查看t的類型 list = t('a') # 循環獲取每個t對象中的所有a標簽,並保存到一個列表中 print(list) print(type(list)) print('每個p標簽的第一個a標簽的內容:', list[0].string) # a標簽保存到列表后,便可以利用列表方法解析出其中的具體每個a標簽對象,並利用.string獲取標簽字符串
接下來就可以正式編寫爬蟲了:
分析網頁源代碼
可以看到需要的一些信息如大學排名、大學名稱、地址、分數等分別在如圖標注的地方,每個大學信息所在的標簽結構如下:所有大學信息都在<tbody>標簽下,每個大學都在各自的<tr>標簽,然后大學自身的排名、名稱、地址等信息都分別由一個<td>標簽包裹。
思路如下:先找到<tbody>下的所有標簽內容,然后再從中找出所有<tr>標簽內容(為什么不直接用find_all()找<tr>?因為不只有我們需要的大學信息用到了<tr>標簽,<tbody>之外也有用到<tr>標簽來包裹內容的)。
我要把每個學校的“排名、名稱、地址、分數”的值都取出來,並且把每組數據都各自裝在一個列表中,然后再把每個列表依次加到一個大列表里
(1)直接處理數據
from bs4 import BeautifulSoup import requests import bs4 url = 'http://www.zuihaodaxue.com/shengyuanzhiliangpaiming2018.html' r = requests.get(url) r.encoding = r.apparent_encoding # 轉換編碼,不然中文會顯示亂碼,也可以r.encoding = 'utf-8' html = r.text soup = BeautifulSoup(html, 'html.parser') # 獲取爬取網頁的BeautifulSoup對象 for tr in soup.find('tbody').children: if isinstance(tr, bs4.element.Tag): td = tr('td')
print(td) t = [td[0].string, td[1].string, td[2].string, td[3].string] # 把每個學校解析出的數據各自裝到一個列表中
print(t)
打印td的結果:
打印t的結果如下,其實排名信息已經可以看出來了
然后依次把每個大學信息寫入一個文本文檔:
from bs4 import BeautifulSoup import requests import bs4 url = 'http://www.zuihaodaxue.com/shengyuanzhiliangpaiming2018.html' r = requests.get(url) r.encoding = r.apparent_encoding # 轉換編碼,不然中文會顯示亂碼,也可以r.encoding = 'utf-8' html = r.text soup = BeautifulSoup(html, 'html.parser') # 獲取爬取網頁的BeautifulSoup對象 for tr in soup.find('tbody').children: if isinstance(tr, bs4.element.Tag): td = tr('td') t = [td[0].string, td[1].string, td[2].string, td[3].string] # 把每個學校解析出的數據各自裝到一個列表中print(t) with open('D:/test.txt','a') as data: # 以'a'模式打開文件,即可不停的追加寫入而不改變原內容
print(t, file=data)
(2)把代碼封裝,寫到函數中
# coding:utf-8 import requests import bs4 from bs4 import BeautifulSoup def get_html(url):
"""定義獲取網頁源碼函數""" try: r = requests.get(url, timeout=20) r.encoding = r.apparent_encoding return r.text except: return None def get_data(html, list):
"""定義從網頁源碼獲取數據並處理數據函數""" soup = BeautifulSoup(html, 'html.parser') for tr in soup.find('tbody').children: if isinstance(tr, bs4.element.Tag): td = tr('td') t = [td[0].string, td[1].string, td[2].string, td[3].string] # 把每個學校解析處的數據各自裝到一個列表中 list.append(t) # 把每個學校信息列表都追加到一個大列表中,方便后面寫入文件 # return list # 不能加return,造成的后果就是第一次循環時就把結果返回出去了,只取到了第一條數據 def write_data(ulist, num): # num參數,控制提取多少組數據寫入文件
"""定義把數據寫入文件函數""" for i in range(num): u = ulist[i] with open('D:/test.txt', 'a') as data: print(u, file=data) if __name__ == '__main__': list = [] # 我之前是把list=[]放到get_data()函數的for循環里面了,導致每次循環都會先清空列表,然后再追加數據,結果最后遍歷完只剩最后一組數據。。。
url = 'http://www.zuihaodaxue.com/shengyuanzhiliangpaiming2018.html'
html = get_html(url)
get_data(html, list)
write_data(list, 20)
把結果輸出到屏幕(老師給出的代碼)
# coding: utf-8 import requests from bs4 import BeautifulSoup import bs4 # def GetHTMLText(url): # try: # r = requests.get(url, timeout=30) # r.raise_for_status() # r.encoding = r.apparent_encoding # return r.text # except: # return "" # # # def fillUnivList(ulist, html): # soup = BeautifulSoup(html, "html.parser") # for tr in soup.find('tbody').children: # if isinstance(tr, bs4.element.Tag): # tds = tr('td') # ulist.append([tds[0].string, tds[1].string, tds[2].string, tds[3].string]) # # # # def printUnivList(ulist, num): # print("{:^10}\t{:^6}\t{:^10}\t{:^10}".format('排名', '學校名稱', '地區', '總分')) # for i in range(num): # u = ulist[i] # print("{:^10}\t{:^6}\t{:^10}\t{:^10}".format(u[0], u[1], u[2], u[3])) # # # def main(): # uinfo = [] # url = 'http://www.zuihaodaxue.com/shengyuanzhiliangpaiming2018.html' # html = GetHTMLText(url) # fillUnivList(uinfo, html) # printUnivList(uinfo, 20) # return uinfo # # # if __name__ == '__main__': # t = main()