1.问题描述

​ 最近在做一个pyqt登录校园网的小项目,想在窗口的状态栏加上当天的天气情况,用爬虫可以很好的解决我的问题。


*最新,发现www.ip.cn采用了js动态加载,所以代码有所变化,但整体思路不变

2.解决思路

​ 考虑到所处位置的不同,需要先获取本地城市地址,然后作为中国天气网的输入,爬取指定城市的天气信息。

​ a. 先通过https://www.ip.cn/爬取本地城市名称

b. 再通过获取本地城市名称作为输入

进入城市页面获取所需信息即可,看起来不难,不就是爬、爬吗

3.思路实现

​ a 很容易实现,直接上代码

def download_page(url):
"""
获取网页源代码
"""
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0'}
html = requests.get(url, headers=headers)
# print(html.text)
return html.text def get_city_name(html):
"""
对网页内容进行解析并分析得到需要的数据
"""
selector = etree.HTML(html) # 将源码转换为能被XPath匹配的格式
_info = selector.xpath('//script')[1].text # 由于网页采用动态加载,“所在的地理位置”跑到了<script>中
location = re.findall(r"<code>(.*?)</code>", _info)[1]
location = location.split(" ")[0]
if location in municipality:
city = location[:-1] # 直辖市的话不取'市',不然天气结果会不准
else:
for i, char in enumerate(location):
if char == "区" or char == "省":
index = i + 1
break
city = location[index:-1] # 取'省'后面一直到'市'中间的城市名称用作天气搜索
return city

我的ip会返回‘合肥’,北京,上海这些直辖市和一些自治区需要特别处理一下才能获得所需城市名,这里必须返回城市名称,不然直接搜索会出现这样,得不到任何天气data,好吧,能给我天气情况就行,要什么自行车:

b 的话需要2步走,首先获取城市对于编码

在输入合肥点击搜索之后,通过观察网络请求信息,浏览器发送了一个GET请求,服务器响应数据中包含城市码,如下图

def get_city_code(city='合肥'):
"""
输入一个城市,返回在中国天气网上城市所对应的code
"""
try:
parameter = urlparse.parse.urlencode({'cityname': city})
conn = http.client.HTTPConnection('toy1.weather.com.cn', 80, timeout=5)
conn.request('GET', '/search?'+parameter)
r = conn.getresponse()
data = r.read().decode()[1:-1]
json_data = json.loads(data)
code = json_data[0]['ref'].split('~')[0] # 返回城市编码
return code
except Exception as e:
raise e

模拟发送请求即可,头信息在这,这里&callback和&_参数可以不要,然后对响应做下处理,因为得到的JSON数据字符串,需要将字符串转为JSON数据格式,这就能得到城市码了。

终于到了最后一步,也是最烦,不对,是最难的一步,获取城市天气信息,按说操作思路和上面差不多,就是分析页面数据然后找标签就行了,,,

可是这个网站竟然是javascript动态加载的,获取的页面图上所指的内容啥都没,这要我怎么获取天气情况,对于只会爬取非动态网页的小白,只能留下没技术的眼泪。

​就这样,又去学了一点动态爬取网站的基础知识,通过一个一个请求分析查看,终于找到了我所要的信息,其实只要看响应就行了,服务器js返回的值都在这能看到。

接着套路和上面一样,发送一个GET请求等服务器返回数据我们拿来用就行了

。。。。。。。。

问题又出现了,这次又是403,还有有人已经走过这条路了,在GET请求加上headers就行了,不让服务器发现这是爬虫,而是认为这是浏览器在请求,结果发现只需要在请求头加上Referer参数就行,看来服务器并没有真正做到反扒。。。。

def get_city_weather(city_code):
""" 通过城市编码找到天气信息
"""
try:
url = 'http://www.weather.com.cn/weather1dn/' + city_code + '.shtml'
# 这里选择的体验新版,所以是1dn,旧版是1d
headers = { "Referer": url }
conn = http.client.HTTPConnection('d1.weather.com.cn', 80, timeout=5)
conn.request('GET', '/sk_2d/' + city_code + '.html', headers=headers)
r = conn.getresponse()
data = r.read().decode()[13:]
weather_info = json.loads(data)
return weather_info
except Exception as e:
raise e

好了,大功告成!

贴一下自己的代码供有需要的人参考:

# -*- coding:utf-8 -*-
# 天气脚本
import json
import re
import urllib as urlparse
import http.client
import requests
from lxml import etree target_url = 'https://www.ip.cn/'
municipality = ['北京市','上海市','重庆市','天津市'] def download_page(url):
"""
获取网页源代码
"""
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0'}
html = requests.get(url, headers=headers)
# print(html.text)
return html.text def get_city_name(html):
"""
对网页内容进行解析并分析得到需要的数据
"""
selector = etree.HTML(html) # 将源码转换为能被XPath匹配的格式
_info = selector.xpath('//script')[1].text # 由于网页采用动态加载,“所在的地理位置”跑到了<script>中
location = re.findall(r"<code>(.*?)</code>", _info)[1] #使用正则表达式获取地理位置信息,在<code></code>标签中
location = location.split(" ")[0]
if location in municipality:
city = location[:-1] # 直辖市的话不取'市',不然天气结果会不准
else:
for i, char in enumerate(location):
if char == "区" or char == "省":
index = i + 1
break
city = location[index:-1] # 取'省'后面一直到'市'中间的城市名称用作天气搜索
return city def get_city_code(city='合肥'):
"""
输入一个城市,返回在中国天气网上城市所对应的code
"""
try:
parameter = urlparse.parse.urlencode({'cityname': city})
conn = http.client.HTTPConnection('toy1.weather.com.cn', 80, timeout=5)
conn.request('GET', '/search?' + parameter)
r = conn.getresponse()
data = r.read().decode()[1:-1]
json_data = json.loads(data)
code = json_data[0]['ref'].split('~')[0] # 返回城市编码
return code
except Exception as e:
raise e def get_city_weather(city_code):
"""
通过城市编码找到天气信息
"""
try:
url = 'http://www.weather.com.cn/weather1dn/' + city_code + '.shtml'
headers = { "Referer": url }
conn = http.client.HTTPConnection('d1.weather.com.cn', 80, timeout=5)
conn.request('GET', '/sk_2d/' + city_code + '.html', headers=headers)
r = conn.getresponse()
data = r.read().decode()[13:]
weather_info = json.loads(data)
return weather_info
except Exception as e:
raise e if __name__ == '__main__':
city = get_city_name(download_page(target_url))
city_code = get_city_code(city)
city_wether = get_city_weather(city_code)
print(city_wether)

PS:第一次写博客,markdown刚学还不是很熟,表达可能也有点欠缺。

每次都是从别人那填好的坑走过,这次要填自己的坑,给别人开路

爬虫-通过本地IP地址从中国天气网爬取当前城市天气情况的更多相关文章

  1. Python-定时爬取指定城市天气(二)-邮件提醒

    目录 一.概述 二.模块重新划分 三.优化定时任务 四.发送邮件 五.源代码 一.概述 上一篇文章python-定时爬取指定城市天气(一)-发送给关心的微信好友中我们讲述了怎么定时爬取城市天气,并发送 ...

  2. Python-定时爬取指定城市天气(一)-发送给关心的微信好友

    一.背景 上班的日子总是3点一线,家里,公司和上班的路径,对于一个特别懒得我来说,经常遇到上班路上下雨了,而我却没带伞,多么痛的领悟.最近对python有一种狂热的学习热情,写了4年多的C++代码,对 ...

  3. 关于python的中国历年城市天气信息爬取

    一.主题式网络爬虫设计方案(15分)1.主题式网络爬虫名称 关于python的中国城市天气网爬取 2.主题式网络爬虫爬取的内容与数据特征分析 爬取中国天气网各个城市每年各个月份的天气数据, 包括最高城 ...

  4. 获取本地IP地址信息

    2012-06-05    /// <summary>         /// 获取本地IP地址信息         /// </summary>         void G ...

  5. 获取Mac、CPUID、硬盘序列号、本地IP地址、外网IP地址OCX控件

    提供获取Mac.CPUID.硬盘序列号.本地IP地址.外网IP地址OCX控件 开发语言:vc++ 可应用与WEB程序开发应用 <HTML><HEAD><TITLE> ...

  6. c、c++混编实现查询本地IP地址

    一.思路 1.要想得到本地IP地址,可以通过本机名来查询,所以首先得得到本机名. 2.牵涉到IP地址,首先想到牵涉到网络协议,因此得加载套接字协议,所以先使用WSAStartup函数完成对Winsoc ...

  7. python获取本地ip地址的方法

    #_*_coding:utf8_*_ #以下两种方法可以在ubuntu下或者windows下获得本地的IP地址 import socket # 方法一 localIP = socket.gethost ...

  8. C# — 动态获取本地IP地址及可用端口

    1.在VS中动态获取本地IP地址,代码如下: 2.获取本机的可用端口以及已使用的端口:

  9. Android 获得本地IP地址、外网IP地址、本设备网络状态信息、本地Mac地址

    本地内网IP和外网IP的区别: 根据我的经验一台电脑需要两个ip才可以上网,一个是本地的内网ip 一个是外网的ip 本地的ip 一般是192.168.1.2这种样子  只要在不同的路由器上可以重复 外 ...

随机推荐

  1. ASP.NET Core 微服务初探[1]:服务发现之Consul

    ASP.NET Core 微服务初探[1]:服务发现之Consul   在传统单体架构中,由于应用动态性不强,不会频繁的更新和发布,也不会进行自动伸缩,我们通常将所有的服务地址都直接写在项目的配置文件 ...

  2. GRE与VXLAN

    一 GRE 1.1 概念 GRE全称是Generic Routing Encapsulation,是一种协议封装的格式,具体格式内容见:https://tools.ietf.org/html/rfc2 ...

  3. 深入剖析Redis系列:Redis数据结构与全局命令概述

    前言 Redis 提供了 5 种数据结构.理解每种数据结构的特点,对于 Redis 的 开发运维 非常重要,同时掌握 Redis 的 单线程命令处理 机制,会使 数据结构 和 命令 的选择事半功倍. ...

  4. vue.js实战——$event

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. docker下载镜像received unexpected Http status:500 Internal Server Error

    解决办法 1.就是网上说的 关闭selLinue ,但是对我就没用 2.就是不使用镜像加速 ,但是出现连接超时 3.就是加上具体版本号 结果就成功了 [root@localhost ~]# docke ...

  6. SpringBoot与Mybatis整合的设置

    Mybatis和Spring Boot的整合有两种方式: 第一种:使用mybatis官方提供的Spring Boot整合包实现,地址:https://github.com/mybatis/spring ...

  7. Amazon SNS (Simple Notification Service) Using C# and Visual Studio

    SNS (Amazon Simple Notification Services) Amazon SNS (Amazon Simple Notification Services) is a noti ...

  8. Civil 3D .NET二次开发第11章代码升级至2018版注意事项

    原来涉及2017的,均需要改为2018 原来的21改为22 代码中AeccXUiLand.AeccApplication.11.0"改为AeccXUiLand.AeccApplication ...

  9. Vue——报错总结

    [Vue warn]: Cannot find element: #app [报错原因] 1. 把对应js放在了head标签里面,页面没有加载完成就进行渲染,导致找不到#app. 2.加了<te ...

  10. CF618G(利用浮点数精度+矩乘优化DP)

    这题真的太神辣,%了一发题解,原来还能这么搞QWQ 设\(A_{i,j}\)表示不加任何限制时,第\(i\)个格子会出现权值为\(j\)的史莱姆的概率,则有: \[A_{i,j}=A_{i,j-1}* ...