实战|Python轻松实现动态网页爬虫(附详细源码)
J哥一向注重理论与实践相结合,知其然也要知其所以然,才能以不变应万变。
所谓的动态网页,是指跟静态网页相对的一种网页编程技术。静态网页,随着html代码的生成,页面的内容和显示效果就基本上不会发生变化了——除非你修改页面代码。而动态网页则不然,页面代码虽然没有变,但是显示的内容却是可以随着时间、环境或者数据库操作的结果而发生改变的。——来源百度百科
动态网页具有减少工作量、内容更新快、可完成功能多等特点,被很多公司所采用,比如狗东、某宝、某瓣、某乎等等。
1. 解析接口
只要是有数据发送过来,那肯定是有发送到服务器的请求的吧。我们只需找出它悄悄加载出的页面的真实请求即可。特点:爬取速度快,爬取的数据干净,有些网站解析难度较大。
2. Selenium
selenium是什么呢?它本来是个自动化测试工具,但是被广泛的用户拿去爬虫了。它是一个工具,这个工具可以用代码操作浏览器,比如控制浏览器的下滑、模拟鼠标点击等。特点:代码较简单,爬取速度慢,容易被封ip。
怎么说了那么多理论,说实话J哥也不想那么啰嗦。可是吧,这些东西经常会被问到,干脆直接写下来,下次还有人问就直接把这篇文章发给他,一劳永逸!
OK,咱们继续回到王律师的part。王律师作为一名资深律师,深知研究法院历年公示的开庭信息、执行信息等对于提升业务能力具有重要作用。于是,他兴高采烈地打开了一个法院信息公示网页。
长这样:
然后,他根据J哥之前写的爬虫入门文章去爬数据,成功提取了第一页,内心无比之激动。
紧接着,他加了个for循环,想着花个几分钟时间把此网站2164页共计32457条开庭公告数据提取到excel里。
然后,也就没有然后了。各位看了前面的理论部分应该也知道了,他这是 AJAX动态加载的网页。无论你怎么点击下一页,url是不会变化的。你不信我点给你看看,左上角的url像山一样矗立在那:
既然如此,那我们就开启爬虫的正确姿势吧,先用解析接口的方法来写爬虫。
首先,找到真实请求。右键检查,点击Network,选中XHR,刷新网页,选择Name列表中的jsp文件。没错,就这么简单,真实请求就藏在里面。
我们再仔细看看这个jsp,这简直是个宝啊。有真实请求url,有请求方法post,有Headers,还有Form Data,而From Data表示给url传递的参数,通过改变参数,咱们就可以获得数据!为了安全,我把自个Cookie打了个马赛克,机智的朋友也许发现了我顺带给自个打了个广告。
我们再仔细看看这些参数,pagesnum参数不就是代表页数嘛!王律师顿悟,原来他心心念念的翻页在这里!跨过千山万水终于找到你!我们尝试点击翻页,发现只有pagesnum参数会变化。
既然发现了它,那就赶紧抓住它。J哥以迅雷不及掩耳势打开PyCharm,导入了爬虫所需的库。
1from urllib.parse import urlencode
2import csv
3import random
4import requests
5import traceback
6from time import sleep
7from lxml import etree #lxml为第三方网页解析库,强大且速度快
构造真实请求,添加Headers。这里J哥没有贴自己的User-Agent和Cookie,主要是一向胆小甚微的J哥害怕啊。
1base_url = 'http://www.hshfy.sh.cn/shfy/gweb2017/ktgg_search_content.jsp?' #这里要换成对应Ajax请求中的链接
2
3headers = {
4 'Connection': 'keep-alive',
5 'Accept': '*/*',
6 'X-Requested-With': 'XMLHttpRequest',
7 'User-Agent': '你的User-Agent',
8 'Origin': 'http://www.hshfy.sh.cn',
9 'Referer': 'http://www.hshfy.sh.cn/shfy/gweb2017/ktgg_search.jsp?zd=splc',
10 'Accept-Language': 'zh-CN,zh;q=0.9',
11 'Content-Type': 'application/x-www-form-urlencoded',
12 'Cookie': '你的Cookie'
13}
构建get_page函数,自变量为page,也就是页数。以字典类型创建表单data,用post方式去请求网页数据。这里要注意要对返回的数据解码,编码为'gbk',否则返回的数据会乱码!另外我还加了异常处理优化了下,以防意外发生。
1def get_page(page):
2 n = 3
3 while True:
4 try:
5 sleep(random.uniform(1, 2)) # 随机出现1-2之间的数,包含小数
6 data = {
7 'yzm': 'yxAH',
8 'ft':'',
9 'ktrqks': '2020-05-22',
10 'ktrqjs': '2020-06-22',
11 'spc':'',
12 'yg':'',
13 'bg':'',
14 'ah':'',
15 'pagesnum': page
16 }
17 url = base_url + urlencode(data)
18 print(url)
19 try:
20 response = requests.request("POST",url, headers = headers)
21 #print(response)
22 if response.status_code == 200:
23 re = response.content.decode('gbk')
24 # print(re)
25 return re # 解析内容
26 except requests.ConnectionError as e:
27 print('Error', e.args) # 输出异常信息
28 except (TimeoutError, Exception):
29 n -= 1
30 if n == 0:
31 print('请求3次均失败,放弃此url请求,检查请求条件')
32 return
33 else:
34 print('请求失败,重新请求')
35 continue
构建parse_page函数,对返回的网页数据进行解析,用Xpath提取所有字段内容,保存为csv格式。有人会问为啥J哥这么喜欢用Xpath,因为简单好用啊!!!这么简单的网页结构搞个正则大法装x,J哥我做不到啊。
1def parse_page(html):
2 try:
3 parse = etree.HTML(html) # 解析网页
4 items = parse.xpath('//*[@id="report"]/tbody/tr')
5 for item in items[1:]:
6 item = {
7 'a': ''.join(item.xpath('./td[1]/font/text()')).strip(),
8 'b': ''.join(item.xpath('./td[2]/font/text()')).strip(),
9 'c': ''.join(item.xpath('./td[3]/text()')).strip(),
10 'd': ''.join(item.xpath('./td[4]/text()')).strip(),
11 'e': ''.join(item.xpath('./td[5]/text()')).strip(),
12 'f': ''.join(item.xpath('./td[6]/div/text()')).strip(),
13 'g': ''.join(item.xpath('./td[7]/div/text()')).strip(),
14 'h': ''.join(item.xpath('./td[8]/text()')).strip(),
15 'i': ''.join(item.xpath('./td[9]/text()')).strip()
16 }
17 #print(item)
18 try:
19 with open('./law.csv', 'a', encoding='utf_8_sig', newline='') as fp:
20 # 'a'为追加模式(添加)
21 # utf_8_sig格式导出csv不乱码
22 fieldnames = ['a', 'b', 'c', 'd', 'e','f','g','h','i']
23 writer = csv.DictWriter(fp,fieldnames)
24 writer.writerow(item)
25 except Exception:
26 print(traceback.print_exc()) #代替print e 来输出详细的异常信息
27 except Exception:
28 print(traceback.print_exc())
最后,遍历一下页数,调用一下函数。OK,搞定!
1 for page in range(1,5): #这里设置想要爬取的页数
2 html = get_page(page)
3 #print(html)
4 print("第" + str(page) + "页提取完成")
我们来看一下最终效果:
首先,把相关库导进来。
1from lxml import etree
2import time
3from selenium import webdriver
4from selenium. webdriver.support.wait import WebDriverWait
5from selenium.webdriver.support import expected_conditions as EC
6from selenium.webdriver.common.by import By
然后,用chromedriver驱动打开这个网站。
1def main():
2 # 爬取首页url
3 url = "http://www.hshfy.sh.cn/shfy/gweb2017/flws_list.jsp?ajlb=aYWpsYj3D8crCz"
4 # 定义谷歌webdriver
5 driver = webdriver.Chrome('./chromedriver')
6 driver.maximize_window() # 将浏览器最大化
7 driver.get(url)
参考链接:
Ajax:https://www.w3school.com.cn/php/php_ajax_intro.asp;
Ajax_json:https://www.jianshu.com/p/1897a8068dfb;
Examples:https://www.zhihu.com/question/46528604?sort=created
PS:在公众号【小詹学Python】后台回复"law"即可自动获取本项目完整代码啦~
由于微信平台算法改版,公号内容将不再以时间排序展示,如果大家想第一时间看到我们的推送,强烈建议星标我们和给我们多点点【在看】。星标具体步骤为:
(1)点击页面最上方“小詹学Python”,进入公众号主页。
(2)点击右上角的小点点,在弹出页面点击“设为星标”,就可以啦。
感谢支持,比心。