爬虫获取不到网页完整源码_你的第一只网络爬虫

 我相信有相当大部分人,学习python最初动机,就是做一个网络爬虫,虽然python的主要强项是数据分析方面(至少我是这样认为的),但这并不妨碍它成为目前最主流的网络爬虫编写语言。

网络爬虫是什么?

——网络上一切看得见的,或看不见的数据,无论是用浏览器还是App或是其他工具打开的,只要是数据,理论上都是通过“数据包”的形式在网络上传播的,爬虫就是用一堆代码,去获取那些数据包,然后把它们解析成我们能看得懂的样子。

好像比较拗口……,再用大白话说一次:爬虫,就是数据获取工具。而且,出于各种原因,大家最好在正式场合不要说“爬虫技术”,而要说“数据获取技术”。

正式开始,先介绍一下python在数据获取方面,我常用的包有哪些:

  • requests  用于获取页面的原始代码

  • lxml(etree)  用于对网页标签进行定位,可以解析出需要的数据

  • bs4(BeautifulSoup)  同上,最早的爬虫包,现在用的少了,但在某些场景还是有它的用处

  • selenium(webdriver)  模拟浏览器,应付比较多渲染效果、有用户登录或验证码的场景

在掌握了常用的包之后,数据获取的大致流程如下(以获取网站数据举例):

  • 首先了解自己需要的数据是哪些,百度一下这些数据大概都在哪些网站上有,迅速人工浏览一下这些目标网站的首页和内页情况,观察一下页面的复杂程度、是否有大量渲染效果、是否有用户登录、是否需要验证码……等等,然后确定一个最终的目标网站,要求是在拥有我们需要的数据的前提下,数据获取难度相对较小。

  • 用requests(或webdriver)把页面获取下来(如果有渲染、用户登录或验证码,就需要使用相应工具处理后才能获取到),对目标网站的网页代码进行分析,定位出我们需要的数据,并把这些数据用dict类型或list类型组织起来,形成对我们有用的数据包。

  • 把组织好的数据,存到mongoDB或excle、txt文件里面去,方便我们使用。

做完这些,爬虫基本上就做完了。通常一个爬虫代码,只能用于一个网站,我们通常称负责页面解析的代码为“爬虫模板”。网站页面的代码如果发生更新,会导致爬虫模板不能获取到正确的数据,甚至直接报错,需要对爬虫模板进行相应的更新。对于需要持续获取数据的爬虫,这个“博弈”过程会反复出现。

循例,我们还是上一个例子,来简单介绍一下上面的流程:

第一步  确定需求、确定目标网站:

比如,我们需要历史上广州的天气情况数据,我们先百度一下“全国天气历史数据”,我们大概可以找到以下这些网站:

中国天气网:http://www.weather.com.cn/forecast/

IP138天气查询:https://qq.ip138.com/weather/history.htm

天气后报网:http://www.tianqihoubao.com/

……

经过对每个网站进行一些简单的点击,翻看,感觉天气后报网比较适合爬取,就把它确定为目标网站吧。

第二步  直接上代码:

刚开始,我们应该是不知道这个网站是否有什么防爬技术,先用最简单的方法去试试,看能不能work得了

import requestsurl = 'http://www.tianqihoubao.com/lishi/guangzhou/month/201904.html'rs = requests.get(url=url)urlText = rs.textprint(urlText)

怀着忐忑的心情运行之,发现输出是这样的:

54e814195dd3b8e14d1177980f55e837.png

OK,it works,可以进行下一步工作了(如果没出现这种网页源码,就要尝试一下模拟浏览器了)。

接下来,我们进行页面分析,当我们在浏览器地址栏中输入url地址:

http://www.tianqihoubao.com/lishi/guangzhou.html时,出现以下页面

0a09396fc02c42d2c6a0cfada30ee93f.png

历史上每个月,是这个页面上的一个链接,如果要获取全部的历史天气记录,我们需要先把这个页面上所有的历史天气链接地址,获取下来,存到一个list类型的变量中。

然后,我们点击某一个月的链接,看看内页的情况,出现以下页面

ef3ff21159e06e0d2a10fb7a02b5c250.png

这就是我们要获取的数据了。

在这个页面上,我们按F12(谷歌浏览器),弹出开发者工具,在Elements栏目下,点最左上角的那个小箭头,然后把鼠标移到页面上我们要获取的任意一个数据上面,像这样

119a299fd2a08551da4f3377066fadf9.png

这时,开发者工具的Elements窗口中的内容,就会定位到这个数据元素在页面源代码中的位置,类似这样

2f2248ec12e017703b3b04e4bba77684.png

我们发现,这个页面是用table+tr+td的模式排放数据的(这个办法比较老土,居然这个网站还在用O(∩_∩)O),接下来,就可以编写爬虫了。

先去拿每个月的url地址,代码如下:

import requestsfrom lxml import etreeclass My_Spider:    def __init__(self):        self.base_url = 'http://www.tianqihoubao.com'        self.month_home = 'http://www.tianqihoubao.com/lishi/guangzhou.html'    def get_month_list(self):        rs = requests.get(url=self.month_home)        listUrlText = rs.text        listUrlTextEtree = etree.HTML(listUrlText)        month_list = listUrlTextEtree.xpath("//div[@class='wdetail']/div[@class='box pcity']/ul/li/a/@href")        return month_list    def get_weather(self):        month_list = self.get_month_list()        for mon in month_list:            month_url = self.base_url + mon            print(month_url)if __name__ == '__main__':    my_spider = My_Spider()    my_spider.get_weather()

打印结果如下:

64a83a945ebddf239c51ad5cbcb556b4.png

你会发现这些地址有长有短,原因是那些短一些的地址中间少了‘/lishi/’。这种问题,在一些老式的网站上经常出现,要解决它。

完整代码(带详细注释):

# coding:utf-8import requestsfrom lxml import etreeimport pandas as pdclass My_Spider:    '''    定义一个类的初始化函数,在函数中定义两个属性,    1、目标网站的地址,这个在后面有用    2、目标城市的每月天气的地址列表    '''    def __init__(self):        self.base_url = 'http://www.tianqihoubao.com'        self.month_home = 'http://www.tianqihoubao.com/lishi/guangzhou.html'    '''    定义一个获取目标城市每月天气的地址列表的函数,    返回值是一个list类型的变量,里面是url地址列表(url不完整,将在后面补全)    '''    def get_month_list(self):        rs = requests.get(url=self.month_home)  # 访问url        listUrlText = rs.text  # 将获得的数据包变成文本形式的网页源码        listUrlTextEtree = etree.HTML(listUrlText)  # 将网页源码文本转换成树形结构,方便xpath定位元素        month_list = listUrlTextEtree.xpath("//div[@class='wdetail']/div[@class='box pcity']/ul/li/a/@href")        print(month_list)        return month_list    '''    进入每日天气列表,并获取每日天气情况    '''    def get_weather(self):        month_list = self.get_month_list()        b_str = '/lishi/'  # 我们发现获得的地址有些缺少这个字符串        weather_group = []        for mon in month_list:            month_url = self.base_url + mon  # 组合好url地址            # 将不完整的url地址补全            if b_str not in month_url:                month_url = self.base_url + b_str + mon            print(month_url)            rs = requests.get(url=month_url)  # 访问url            listUrlText = rs.text  # 将获得的数据包变成文本形式的网页源码            listUrlTextEtree = etree.HTML(listUrlText)  # 将网页源码文本转换成树形结构,方便xpath定位元素            data_body = listUrlTextEtree.xpath("//table/tr")  # 获取需要的大的数据区域            for d_body in data_body[1:]:  # 去掉第一行表头                item = {}                # 获取每一行、每一格数据,组成dict数据包                item['日期'] = d_body.xpath("./td[1]/a/text()")[0].strip().replace('\r\n', '').replace(' ', '')                item['天气状况'] = d_body.xpath("./td[2]/text()")[0].strip().replace('\r\n', '').replace(' ', '')                item['温度'] = d_body.xpath("./td[3]/text()")[0].strip().replace('\r\n', '').replace(' ', '')                item['风力风向'] = d_body.xpath("./td[4]/text()")[0].strip().replace('\r\n', '').replace(' ', '')                print(item)                weather_group.append(item)  # 将数据包放进list类型的变量中        weather_DF = pd.DataFrame(weather_group)  # 将获取到的数据,转成数据表        weather_DF.to_csv('广州历史天气.csv', encoding='utf-8-sig')  # 数据持久化,即把数据表存成csv文件if __name__ == '__main__':    my_spider = My_Spider()    my_spider.get_weather()

在爬取过程中,在控制台监控程序运行情况,工作正常:

5eb82dd21a195c4c504ac96ba7ea2aaf.png

最后,我们存下来的CSV文件打开看一下,确保数据存储完成。

18411dd86b05c4c65c33482bf3fa5839.png

——  分割线  ——

当然,这是一个比较简单的爬虫,没有复杂的反防爬技术,但制作一个爬虫的基本流程都有了,复杂爬虫只是在每一步上都需要多付出一些劳动而已。

打完收工!看官可满意否?

2e9dbde00c7e49dce3ce81e63838dc37.png


版权声明:本文为weixin_39554891原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。