在简单完成了基金净值爬取以后, 我们对中间的过程可能产生了很多疑惑, 即使完成了目标, 也仅仅是知其然而不知其所以然, 而为了以后爬虫任务的顺利进行, 对爬虫过程中所涉及的原理进行掌握是十分有必要的.
本文将会针对之前爬虫过程中所涉及到的几个爬虫原理进行简单的阐述.
url 究竟是什么? 它的构成有什么规律可循?
URL 和 URI
在访问任何一个网页时, 我们都需要一个网页链接(如百度: www.baidu.com), 这就相当于网页的 "家庭地址" 一样, 只有在知道了这个 "地址", 我们才能看到 "这户人家" 长得什么样. 而这个 "地址" 在大部分时候也被称为 URL, 全称为 Universal Resource Locator, 即统一资源定位符.
除了 URL, 还有一个极少听到的名词 --URI, 全称为 Uniform Resource Identifier, 即统一资源标志符.
以获取基金代码列表时用到的链接为例 --
http://fund.eastmoney.com/js/fundcode_search.js 是天天基金网基金代码的数据存储链接, 它是一个 URL, 也是一个 URI. 即有这样的数据资源, 我们用 URL/URl 来唯一指定了它的访问方式, 这其中包括了访问协议 http, 访问路径 (/ 即根目录) 和资源名称 fundcode_search.JS. 通过这样的一个链接, 我们便可以从互联网上找到这个资源, 这就是 URL/URI.
URL 是 URI 的自己, 也就是说每个 URL 都是 URI, 但不是每一个 URI 都是 URL,URI 的子集中还包括 URN, 它在目前的互联网中用得非常少, 几乎所有的 URI 都是 URL, 因此, 一般的网页链接我们都可以直接, 也惯称为 URL.
URL 的解析
在爬取基金代码和基金净值数据时, 仔细观察相关的 URL, 我们可以发现它们的构成并非是无规律可循的. 而事实上, URL 的构成也确实存在一套统一的标准.
protocol://domain[:port]/path/[?parameters]#fragment
protocol 协议: 标明了请求需要使用的协议, 通常使用的是 HTTP 协议或者安全协议 HTTPS. 其他协议还有 mailto: 用户打开邮箱的客户端, 和 ftp: 用来做文件的转换, file 用来获取文件, data 获取外部资源等
domain 域名: 标明了需要请求的服务器的地址, 一个 URL 中也可以使用 IP 地址作为域名使用
port 端口: 标明了获取服务器资源的入口端口号用于区分服务的端口, 一台拥有 IP 地址的服务器可以提供许多服务, 比如 web 服务, FTP 服务, SMTP 服务等. 那么, 服务器的资源通过 "IP 地址 + 端口号" 来区分不同的服务. 如果把服务器比作房子, 端口号可以看做是通向不同服务的门. 端口不是一个 URL 必须的部分, 如果省略端口部分, 将采用默认端口, 一般为 80.
path 路径: 表示服务器上资源的路径, 从域名后的最后一个 "/" 开始到 "?" 为止, 是文件名部分, 如果没有 "?", 则是从域名后的最后一个 "/" 开始到 "#" 为止, 是文件部分, 如果没有 "?" 和 "#", 那么从域名后的最后一个 "/" 开始到结束, 都是文件名部分. 过去这样的路径标记的是服务器上文件的物理路径, 但是现在, 路径表示的只是一个抽象地址, 并不指代任何物理地址. 文件名部分也不是一个 URL 必须的部分, 如果省略该部分, 则使用默认的文件名.
parameter 参数: 从 "?" 开始到 "#" 为止之间的部分为参数部分, 又称搜索部分, 查询部分. 这些参数是以键值对的形式, 通过 & 符号分隔开来, 服务器可以通过这些参数进行相应的个性化处理.
- def get_fundcode():
- '''
- 获取 fundcode 列表
- :return: 将获取的 DataFrame 以 csv 格式存入本地
- ''' url ='http://fund.eastmoney.com/js/fundcode_search.js'
- r = requests.get(url)
- cont = re.findall('var r = (.*])', r.text)[0] # 提取 list
- ls = JSON.loads(cont) # 将字符串个事的 list 转化为 list 格式
- fundcode = pd.DataFrame(ls, columns=['fundcode', 'fundsx', 'name', 'category', 'fundpy']) # list 转为 DataFrame
- fundcode = fundcode.loc[:, ['fundcode', 'name', 'category']]
- fundcode.to_csv('./ 案例 / 基金净值爬取 / fundcode.csv', index=False)
- def get_one_page(fundcode, pageIndex=1):
- '''
- 获取基金净值某一页的 html
- :param fundcode: str 格式, 基金代码
- :param pageIndex: int 格式, 页码数
- :return: str 格式, 获取网页内容
- ''' url ='http://api.fund.eastmoney.com/f10/lsjz' cookie ='EMFUND1=null; EMFUND2=null; EMFUND3=null; EMFUND4=null; EMFUND5=null; EMFUND6=null; EMFUND7=null; EMFUND8=null; EMFUND0=null; EMFUND9=01-24 17:11:50@#$长信利广混合A@#$519961; st_pvi=27838598767214; st_si=11887649835514'
- headers = {
- 'Cookie': cookie,
- 'Host': 'api.fund.eastmoney.com',
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
- 'Referer': 'http://fundf10.eastmoney.com/jjjz_%s.html' % fundcode,
- }
- params = {
- 'callback': 'jQuery18307633215694564663_1548321266367',
- 'fundCode': fundcode,
- 'pageIndex': pageIndex,
- 'pageSize': 20,
- }
- try:
- r = requests.get(url=url, headers=headers, params=params)
- if r.status_code == 200:
- return r.text
- return None
- except RequestException:
- return None
- def parse_one_page(HTML):
- '''
- 解析网页内容
- :param html: str 格式, html 内容
- :return: dict 格式, 获取历史净值和访问页数
- '''
- if HTML is not None: # 判断内容是否为 None
- content = re.findall('\((.*?)\)', HTML)[0] # 提取网页文本内容中的数据部分
- lsjz_list = JSON.loads(content)['Data']['LSJZList'] # 获取历史净值列表
- total_count = JSON.loads(content)['TotalCount'] # 获取数据量
- total_page = math.ceil(total_count / 20) #
- lsjz = pd.DataFrame(lsjz_list)
- info = {'lsjz': lsjz,
- 'total_page': total_page}
- return info
- return None
- def main(fundcode):
- '''
- 将爬取的基金净值数据储存至本地 csv 文件
- '''
- HTML = get_one_page(fundcode)
- info = parse_one_page(HTML)
- total_page = info['total_page']
- lsjz = info['lsjz']
- lsjz.to_csv('./ 案例 / 基金净值爬取 /%s_lsjz.csv' % fundcode, index=False) # 将基金历史净值以 CSV 格式储存
- page = 1
- while page < total_page:
- page += 1
- print(lsjz)
- HTML = get_one_page(fundcode, pageIndex=page)
- info = parse_one_page(HTML)
- if info is None:
- break
- lsjz = info['lsjz']
- lsjz.to_csv('./ 案例 / 基金净值爬取 /%s_lsjz.csv' % fundcode, mode='a', index=False, header=False) # 追加存储
- time.sleep(random.randint(5, 10))
- if __name__=='__main__':
- # 获取所有基金代码
- get_fundcode()
- # fundcode = '519961'
- fundcodes = pd.read_csv('./ 案例 / 基金净值爬取 / fundcode.csv', converters={'fundcode': str})
- # 获取所有基金净值数据
- for fundcode in fundcodes['fundcode']:
- print(fundcode)
- main(fundcode)
- time.sleep(random.randint(5, 10))
- View Code
来源: http://www.bubuko.com/infodetail-2955969.html