大家好,今天我来给大家分享一下爬虫。对于网页中几百几千页的数据,手动是不可能,而且容易出错,这时候爬虫就站出来了,一个for循环,完成重复的工作。话不多说,直接进入正题。
------思路部分-----(不想听的可以直接滑下去)
大部分网站都具备一定的反爬措施,首先我们要了解前端与服务器之间的关系。前端,也就是我们自己电脑的浏览器页面,在浏览器页面右击,点检查,我们可以看到很多代码,这些代码经过浏览器的解析,转换为一定的格式显示在屏幕上。服务器是对方的,你要访问一个网页,首先需要浏览器向对方服务器发送一个请求,请求访问,对方服务器确定了可以让你访问,那么就会返回一个数据包到你电脑上,浏览器解析该包,转换为一定的格式显示在屏幕上。
那么你访问一个网址,向对方服务器发送请求后,假如对方服务器不同意你的访问,你就得不到数据。生活中很多情况都会使得对方服务器不同意你的访问,比如你没有权限,又或者你是爬虫程序发起的请求,因为爬虫程序执行速度很快,每秒访问几十次,对方服务器会造成压力。有些网站就会拒绝爬虫的访问。
我们可以通过利用爬虫,伪装成浏览器去向对方发起访问请求,需要注意的是不要过于频繁,后面我会告诉你怎么做不频繁,不着急,一步一步来。千言万语说不清,下面是具体的操作和原理结合,方便理解。
我们以节节草-搜狗百科为例。目标:获取所有图片链接,并下载到本地上。
图1 浏览器页面搜索“节节草”进入搜狗百科,如图1界面。然后右击网页,点“检查”,点“网络”,进入到该页面。第4步很重要!!!。浏览器英文的伙伴自己翻译一下哈。
图2在“标头”里,网址为“请求URL”,如果不是中文,相信你也能看得出。然后往下滑,找到User-Agent和Cookie。留意一下请求方法,是get还是post。
这里可以说一下原理,对方服务器和电脑浏览器是以Cookie作为“密码”的,就是说,你现在访问网页,对方服务器与你浏览器之间有一个约定,你规定时间内的的下一次访问要带着这个Cookie去访问,对方服务器就知道是你,不然下次的访问可能会不成功,因为在对方服务器眼里没有这个“人”。超出规定时间后,Cookie便失效,失效便无法访问,需要重新获取Cookie。这就是为什么同样的代码,今天能运行,过几天却报错的原因之一。
爬虫除了需要url、User-Agent、Cookie,当页面中有多页的时候,需要用到params,页码就存在里面。我的params在“负载”里,有些浏览器会同样放在“标头”下,大概叫“date_form”,我也不是记得很清楚。注意图2的第四步,别不小心点到其他包去找,找不到的。需要你去寻找,找到了下次自然便会。
图3获取到这几个参数后我们就可以开始写爬虫啦!我们说过,用爬虫伪装成浏览器去访问。由于这里没有翻页,params的理解不太明显。后面爬股票的例子再带大家用一下它。
import requests
web = 'https://baike.sogou.com/v223998.htm?fromTitle=%E8%8A%82%E8%8A%82%E8%8D%89'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.39',
'Cookie': 'ssuid=524029849; SUV=001F49727571B9618005EF60C6903; wuid=AAHrKNKSOQAAAAqHQ2VeAAkwA=; SUID=3F605EBE50A00A0000000061B62487; sg_client_ip=120.86.114.181; uname=sg_85db5b10a39bd2e6030faac6e3b0aa; SGINPUT_UPSCREEN=167013632956; cuid=AAG78+VCQwAAAqHSVFAgIANgg=; ABTEST=3|168531092|v17; SNUID=A6A99F092421DD7334D7484249A2FE3; sw_uuid=588057536; IPLOC=CN1200; JSESSIONID=2DBAFF24A2A6A6B65DD9FE450A0B6A; scssuid=778849646'}
params = {'fromTitle': '节节草'}
req = requests.get(url=web,headers=headers,params=params)
print(req.status_code)
可能你看到这篇文章时cookie已经失效,所以代码还是需要你自己替换哈,把你在你浏览器找到的User-Agent和Cookie替换进去。第6行若请求方式为post那就用post,是get就用get。注意 '' : . 等标点符号都需要为英文,中文是不行的。status_code意思是查看状态码,若返回200则表示请求成功,返回400等表示失败,检查代码是否写错了。
图4 请求成功请求成功后我们也可以查看网页代码,看看页面的编码有没有问题。如果出现无法显示的文字,那就是编码错了,也可以提前设置编码后再查看网页代码。如下:
图5 编码无误请求成功后便可以对网页进行解析,用Beautifulsoup。把我们获取的网页req.text放入解析库中,html.parser为解析的方法,还有很多,用这个就好了。soup.findAll(name='img')为在解析后的页面中提取标签为的所有行。
浏览器页面回到“元素”界面(在“网络”同一栏,找一找),发现图片都是以形式存在的,而为标签,用soup.findAll(name='img')提取所有标签为的行。
from bs4 import BeautifulSoup
soup = BeautifulSoup(req.text,'html.parser')
images = soup.findAll(name='img')
for image in images:
print(image)
print('-'*70)
图6 元素界面
很成功能把行提取出来,结果如下:
图7 有3行标签含把代码改一下,提取每行中的src,即图片链接。
from bs4 import BeautifulSoup
soup = BeautifulSoup(req.text,'html.parser')
images = soup.findAll(name='img')
d = []
for image in images:
link = image.get('src')
if 'http' in link:
d.append(link)
else:
d.append('https:'+link)
for i in d:
print(i)
图8 获取图片链接
import time
n = 1
for i in d:
requ = requests.get(url=i)
data = requ.content
road = f"C:/Users/流光、月影/Desktop/{n}.png"
with open(road,'wb') as fi:
fi.write(data)
n += 1
time.sleep(2)
这段小白可能无法理解,可以说,新手看不懂,懂的一看就会。
这里解释一下上面提到的不要过于频繁操作。需要用到time时间库,time.sleep(秒数),程序会等待某秒数后才继续运行下去,这样就能避免对对方服务器造成太大压力。
这种方法叫简单,但也不是所有网站都符合条件,对获取到的数据进行保存时也是需要对pandas库有一定的基础。条件是查询返回的数据是字典形式,可以直接保存到表格中,省去解析网页的步骤。说不清楚,马上带大家体验一下,我们这次爬股票,以新浪财经为例,网址:行情中心_新浪财经_新浪网
图9 网址页面为了体现params的翻页功能,我们选个多页的股票,A股 > 申万一级 > 国防军工 (该图不能放,会违规)
图10 目标页面有4页原理上面讲过,我们直接进入实操。
图11 步骤刷新后,“网络”页面就多了很多“包”,为了找到数据包,点击Fetch/XHR,可以筛掉图片、图标、js等,点击预览可以对每个包进行查看,发现数据存在于最后一个包里,如下:
图12然后在该包下找到网址、headers、cookie和params等参数(在“标头”或“负载”),接着写代码访问网站。
import requests
import pandas as pd
web = 'https://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page=1&num=40&sort=symbol&asc=1&node=sw1_650000&symbol=&_s_r_a=init'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.39',
'cookie': 'SINAGLOBAL=112.86.96.156_16271.9594; SCF=AlO7tPI5YrVX8m0dg5Q2qN5k-Q-3R2u8ZxlEBgCu-adlIFs_iy3-wvvaWI_iBOgGM60RBECC5aOGSdviEWg0.; U_TRS1=00000a0.c5673618.613c1ff5.acffbbc1; SGUID=1638090492543_3970731; FSINALOBAL=112.917.146_162739391.9554; UOR=,,; __bid_n=185ef2af6e3339e9c14207 FPTOKEN=cxKlwt1cjfLNeQxv5JFcGEu3x/mCYdciN/SRpn9C/57vDUnX/otJQ4/rfC0I9H7DuQZ6cQ0EVTw5n3XWw88WuBVp34KWntO0OGOL3/BWVhWenlZEiRwsi3ooa1MXTIrT0RHi3iAwFM35qzWM7AqY6wcfH8zBfFUfgWVoYNydGkq0jNWR1mSuxL+n/GGxMnouGPdpEIqnskVCpu1eZmxpRsdgdGFdufV87pnIPkWjSlRTtuL3nEDydYZgtLI0o4f/Tz2cK1cORhsS01ER5cihUINWFpPWN6V9KtcQ+EW7y8dblBlHLDoC837ZY3cdjZTEVe/u1NYiK+nv7JZ2KiuyjtbQ44KsM4JWX+YHjZrEnlMkxEOxlI+CcXWHYONOiq9XotAyVVaVs3Tg==|Ub5zrecyikK3Z8iJHUvLCMpOomP7Ro6Pmw5tv+sAmZI=|10|37070689b3c1f599e217fd098c56bbfc; Hm_lvt_b82ffdf7cbc70caaacee097b04128ac1=1674754850; SUB=_2AkMUjjRNf8NxqwJRmfsWz3rYR1g_EieKi0sWWJRMyHRl-yD9kql4htRB6Pw4aoguODOQPrEdZ4T4WPJh11FvemoV; SR_SEL=1_511; FINA_V_S_2=sz000001,sh603282; Apache=120.85.187.132_168555452.7554; MONEYFINANCE-SINA-COM-CN-WEB5=; ULV=1681555411117:8:2:2:120.75.17.132_16815554.575554:168555405755'}
params = {'page': '1',
'num': '40',
'sort': 'symbol',
'asc': '1',
'node': 'sw1_650000',
'symbol': '',
'_s_r_a': 'init'}
req = requests.get(url=web,headers=headers)
print(type(req.text))
print(req.text)
图13 结果
可以成功地获取到数据,但是打印数据的类型发现,数据类型为str字符串型,想要像字典和列表一样去提取数据,必须用json库把它转为列表或字典对象。
ls = req.json()
for line in ls:
print(line)
print('-'*70)
图14
把每一行存到excel中,需要有一定的pandas基础,这里我就不一一讲了。有兴趣的可以学一下pandas。
df = pd.DataFrame()
ls = req.json()
for line in ls:
df_li = pd.DataFrame(pd.Series(line))
df = pd.concat([df,df_li.T],ignore_index=True)
df
保存excel文件:
df.to_excel(r"C:\Users\流光、月影\Desktop\gp.xlsx",index=False)
requests库有优点也有缺点,优点是简单,缺点是上面提到过的cookie过期问题,那是不是没有更好的方法了呢?答案是否的,还有个selenium库,直接写代码去驱动浏览器,可以解决cookie的问题,但是比较复杂,有兴趣的伙伴可以了解一下。
爬虫不是一个独立的知识点,python任何一个东西都不是独立的,都是相互关联的,就如爬虫爬到的东西需要保存到电脑,那也是另外一个东西了,如 time 让程序等待,如 os 与系统交互,如 pandas、openpyxl 把数据写入表格等。如果不会保存数据,那么爬虫也毫无意义。学python都是循序渐进,慢慢来,一个一个地学,在这还有很长的路要走,每天学一点,终能登峰。