爬爬爬(一)——网页表格(四种方法)
数据分析爬的最多的就是表格类的了。需要重点掌握。
本篇介绍四种方法:
- find_all
- read_html
- selenium
- 分析网址 JavaScript 请求
一、find_all方法(tr、td)
步骤如下:
- 连接获取网页内容
- 用 BeautifulSoup 处理 html 数据
- 在 soup 里循环搜索需要的 html 元素
- 数据清理
- 写入 csv 文件
1、检查网页元素,观察
在表格上点右键,选择“检查”。在弹出的“开发者工具”中,我们就能看到页面中的每个元素,以及其中包含的内容。
可以看到每行是一个tr标签,每一个内容是tr下的td标签中。(可以做练习,但是实际情况往往不会这么简单,本页所有数据显示在一页上,实际许多数据往往分布在多个不同的页面上,你需要调整每页显示的结果总数,或者遍历所有的页面,才能抓取到完整的数据。)
可以看到表格属性:table class="tableSorter2"
2、连接获取网页内容,用 BeautifulSoup 处理 html 数据
# 导入BeautifulSoup、urllib(request、urlopen)
from bs4 import BeautifulSoup as bs
import urllib
import urllib.request
from urllib.request import urlopen
import csv
urpage="https://www.fasttrack.co.uk/league-tables/tech-track-100/league-table/"
page = urlopen(urpage)
soup=bs(page,"html.parser")
可以用print(soup)来看soup获取是否正确
打印出来可以看到我们找的table中的th(table head)一共8列。分别是rank,company,location等等。
3、在 soup 里循环遍历所有的元素并存储在变量中
存储可以使用空列表+设定表头+循环append元素的方法
循环的结构很简单,主体部分如下(通过两个find_all,对第一个tr列表进行for循环,对第二个td的进行分割截取):
results=table.find_all('tr')
for result in results:
data=result.find_all("td")
loca=data[2].getText()
······
rows.append([rank, company, loca, yearend, anual_sales, sales, staff, comments])
完整如下:
## 空列表加表头
rows=[]
rows.append(["rank","company","loca","yearend","anual_sales","sales","staff","comments"])
## 循环存储
table=soup.find("table")
results=table.find_all('tr')
for result in results:
data=result.find_all("td")
if len(data)==0:
continue
rank=data[0].getText()
company=data[1].getText()
## 如需使用纯公司名,需处理成
## company=data[1].find("span",attrs={"class":"company-name"}).getText()
loca=data[2].getText()
yearend=data[3].getText()
anual_sales=data[4].getText()
sales=data[5].getText()
## 处理sales中符号
sales = sales.strip('*').strip('+').replace(',','')
staff=data[6].getText()
comments=data[7].getText()
rows.append([rank, company, loca, yearend, anual_sales, sales, staff, comments])
print(rows)
4、存储至csv
with open('techtrack100.csv','w', newline='',encoding="utf-8") as f_output:
csv_output = csv.writer(f_output)
csv_output.writerows(rows)
二、pandas库中的read_html方法(table型)
只需不到十行代码,1分钟左右就可以将全部209页共四千多家A股上市公司的信息干净整齐地抓取下来。比采用正则表达式、xpath这类常规方法要省心省力地多。
参数:io可为目标网址,match 为欲匹配的正则表达式,flavor为解析器,header为指定行标题,encoding为读入的编码格式,正确设置才能正确提取表格,否则会出现乱码。
%s代表页数;a:表示A股,把a替换为h,表示港股;把a替换为xsb,则表示新三板。
import pandas as pd
import csv
for i in range(1,178): # 爬取全部177页数据
url = 'http://s.askci.com/stock/a/?reportTime=2017-12-31&pageNum=%s' % (str(i))
tb = pd.read_html(url)[3] #经观察发现所需表格是网页中第4个表格,故为[3]
tb.to_csv(r'C:\Users\xxx\Desktop\1.csv', mode='a', encoding='utf_8_sig', header=1, index=0)
print('第'+str(i)+'页抓取完成')
完整进阶版代码:
# 网页提取函数
def get_one_page(i):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
paras = {
'reportTime': '2019-12-31',
#可以改报告日期,比如2018-6-30获得的就是该季度的信息
'pageNum': i #页码
url = 'http://s.askci.com/stock/a/?' + urlencode(paras)
response = requests.get(url,headers = headers)
if response.status_code == 200:
return response.text
return None
except:
print('爬取失败')
# beatutiful soup解析然后提取表格
def parse_one_page(html):
soup = BeautifulSoup(html,'lxml')
content = soup.select('#myTable04')[0] #[0]将返回的list改为bs4类型
tbl = pd.read_html(content.prettify(),header = 0)[0]
# prettify()优化代码,[0]从pd.read_html返回的list中提取出DataFrame
tbl.rename(columns = {'序号':'serial_number', '股票代码':'stock_code', '股票简称':'stock_abbre', '公司名称':'company_name', '省份':'province', '城市':'city', '主营业务收入(201712)':'main_bussiness_income', '净利润(201712)':'net_profit', '员工人数':'employees', '上市日期':'listing_date', '招股书':'zhaogushu', '公司财报':'financial_report', '行业分类':'industry_classification', '产品类型':'industry_type', '主营业务':'main_business'},inplace = True)
print(tbl)
# return tbl
# rename将表格15列的中文名改为英文名,便于存储到mysql及后期进行数据分析
# tbl = pd.DataFrame(tbl,dtype = 'object') #dtype可统一修改列格式为文本
tbl.to_csv(r'C:\Users\xxx\Desktop\2.csv', mode='a',encoding='utf_8_sig', header=1, index=0)
# 主函数
def main(page):
for i in range(1,page): # page表示提取页数
html = get_one_page(i)
parse_one_page(html)
# 单进程
if __name__ == '__main__':
main(200) #共提取n页
运行快,方便,缺点是不能读取单元格里的href链接。
三、selenium(可爬JavaScript动态加载的table)
思路(selenium基本"可见即可爬"):一次性爬取所有td节点单元内容,按表格列数区隔划开列表内容
1、爬取所有td节点,按照表格列数切分成为表格
from selenium import webdriver
browser = webdriver.Firefox()
browser.get('http://data.eastmoney.com/bbsj/202009/yjbb.html')
element=browser.find_element_by_xpath('//div[@id="dataview"]')
# 提取表格内容td
td_content = element.find_elements_by_tag_name("td") # 进一步定位到表格内容所在的td节点
lst = [] # 存储为list
for td in td_content:
lst.append(td.text)
print(lst) # 输出表格内容
import pandas as pd
# 确定表格列数
col = len(element.find_elements_by_css_selector('tr:nth-child(1) td'))
# 通过定位一行td的数量,可获得表格的列数,然后将list拆分为对应列数的子list
lst = [lst[i:i + col] for i in range(0, len(lst), col)]
# list转为dataframe
df_table = pd.DataFrame(lst)
# 添加url列
# 原网页中打开"详细"链接可以查看更详细的数据,这里我们把url提取出来,方便后期查看
lst_link = []
links = element.find_elements_by_xpath("//a[contains(text(),'详细')]")
for link in links: