python爬虫入门详细教程-采集云南招聘网数据保存为csv文件

目录

    • 网站地址
    • 数据提取技术介绍
    • 采集目标
    • 流程分析
    • python代码实现
    • 教程和代码仅供学习交流,请勿用于其他非法用途!
    • 欢迎加入python学习交流QQ群:891938703

网站地址

https://www.ynzp.com/
这个网址特别适合新手拿来练习,你采集多了还有个验证码页面,验证码是4位数字,很清晰,应该用python自带的ddddorc这个库就能识别出验证码,要是你采集的数据多的话可以先用这个方法试试能不能搞定验证码,我倒是没有时间懒得试了。

数据提取技术介绍

本站点的数据是在后端就把搜索数据渲染在html页面里面,所以可以直接使用xpath来提取htnml网页的数据,没有学习的同学可以先学习一下xpath的相关知识,这里推荐一个知乎的帖子学习xpath:https://zhuanlan.zhihu.com/p/599176415?utm_id=0
保存的数据格式是csv文件,没有了解过这个格式的可以去学习一下,特别简单的一种数据存储格式(每一行内的数据用逗号分割)

采集目标

搜索结果页的数据,以及企业的一些基本信息,具体采集的字段有:岗位名称,工资,工作地点,学历要求,经验要求,工作类型,招聘企业名称,企业行业,企业性质,注册资金,人数规模。
在这里插入图片描述

流程分析

  1. 先在首页的搜索框输入岗位关键字,点击搜索,在弹出的搜索结果页面按F12键或者在网页上右键选择检查,就能打开浏览器的开发者工具了。
    在这里插入图片描述
  2. 在弹出的开发者工具中,选择network选项
    在这里插入图片描述
  3. 确定你当前窗口的链接是在搜索结果页面了,大致的url是:https://www.ynzp.com/search/offer_search_result.aspx?xxxxxxxxxxxxxxxxxxxxxxx
    在这里插入图片描述
  4. 刷新网页,开发者工具的network选项卡就会抓取到浏览器发送给服务器的资源、网络请求
    在这里插入图片描述
  5. 往上拉找到最上面的一个请求,offer_search_result.aspx,为什么上来直接找这个请求呢,可以说是经验。我们是使用网页中的搜索功能查询数据,一般url地址都会带search或者query这种字眼,比如我们要抓包网页登录请求,一般请求url都会带login字眼,我们先根据这种字眼来浏览请求信息,会提高我们的效率。
    在这里插入图片描述
  6. 单击offer_search_result.aspx这个请求,查看请求的信息,首先看得出来这个请求是一个GET请求,那么一般请求的参数都会都会直接拼接在请求的url里面
    在这里插入图片描述
  7. 查看这个请求的响应信息,这一步至关重要,这里我们需要判断出这个请求的响应中是否携带我们所想要采集的数据,如果没有我们所想要的数据,那就只能排查其他的请求了。

点击response这一项
在这里插入图片描述

然后对照浏览器页面,大致浏览这个响应的html数据,发现我们要采集的数据已经存在响应中,就排除了岗位数据是再通过ajax这类请求获取再渲染到页面的,我们只用解析这个html即可得到我们想要的数据。
在这里插入图片描述
当然肯定会有同学说,这么多行代码也不好找啊,其实可以点击代码界面之后,按Ctrl+F键查找,复制网页上的目标采集数据,找得到就说明这个响应存在我们想要的数据了。
在这里插入图片描述
8. 通过前面几步,我们已经确定了offer_search_result.aspx这个请求的响应就包含我们想要的数据,接着我们需要分析一下这个请求都携带什么数据给服务器,服务器才能根据条件查询出对应的结果。因为这个请求是GET请求,请求参数一般都拼接在url中。
可以点击这个请求的payload这一栏查看请求的参数信息
在这里插入图片描述

可以看到这个请求的携带的参数有:

参数名称猜测
keyword看字面意思,根据经验以及盲猜,应该就是查询的关键字
jcity1Hidden这个其实是地区代码,我们不用管也可以
sex这个sex有点猜不到啊,应该是筛选岗位要求的性别条件吧

往下几个参数好像都不重要了,就不分析了。
浏览网页发现,做了分页显示,那我们要采集多页数据的话,需要搞明白网页是怎么获取到下一页的数据的,我们把开发者工具中的网络抓包记录清空
在这里插入图片描述
然后点击网页最底下的分页的第2页,抓第2页的数据包
在这里插入图片描述
第2页的数据我们只用分析请求携带的参数就可以了,这里才会是决定获取的是第几页数据
在这里插入图片描述
发现多了个page的参数,值为2,正好对应我们请求的第2页数据,前面没有这个参数,说明默认就是查询第1页数据,根据这个规律,我们直接修改浏览器上的url中的page参数为999,看页面的结果是如何
在这里插入图片描述
可以看得出来,当查询页码过大时,超过了数据库中查询到的页数,就会显示空的查询结果,那么我们使用python进行逐页采集的时候,只要采集的过程中,分析某一页的岗位数据为空时,就说明这个查询的结果以及采集完毕了。

  1. 因为响应的html中就有数据,可以使用xpath进行数据的提取,先使用xpath对页面数据进行定位提取,这里使用谷歌浏览器插件XPath Helper进行辅助,没有下载安装的去极简插件(https://chrome.zzzmh.cn/#/index)搜索XPath Helper进行下载安装到谷歌浏览器插件中并启用插件
    在这里插入图片描述
    Xpath Hepler非常好用,还会将匹配的页面区域进行渲染成黄色背景
    在这里插入图片描述
    这里要写的xpath有点多,我就列个表格出来,大家可以自己试一下
xpath语句说明区域
//div[@class=“V1Item clearfix”]匹配出每一个岗位在这里插入图片描述
//div[@class=“V1Item clearfix”][1]选中上面匹配结果的第一个结果在这里插入图片描述
//div[@class=“V1Item clearfix”][1]/div[“JobName l clearfix”]/a[@class=“jobName_style”]匹配到岗位名称,需要用@title提取完整岗位名称在这里插入图片描述
//div[@class=“V1Item clearfix”][1]/div[@class=“l ent_style1”]匹配公司名称在这里插入图片描述
//div[@class=“V1Item clearfix”][1]/div[@class=“JobInfo l”]/span匹配城市、学历工作经验、工作类型的要求在这里插入图片描述
//div[@id=“ctl00_ContentPlaceHolder1_AspNetPager1”]/span[8]/a/@href提取到最后一页的url,这里不一定能取到,需要在python里面取最后一个span

这里就不再一一列举了,各位可以根据自己的需求,写出符合自己需求的xpath语句。
我在代码中还采集了企业的资料,具体就是点击企业名称就会跳转到企业资料页面,在这个页面禁用了鼠标左右键,无法右键打开浏览器的控制台,可以直接按F12或者通过浏览器菜单的开发者工具进行打开,这样也可以进入开发者工具,这个页面也是使用xpath可以直接提取到数据的,大家可以根据python代码自己下去分析。

python代码实现

先用pip安装这些库

pip install requests
pip install lxml

具体的python代码实现,支持多个关键字以及指定采集页码数量,采集的数据保存文件与代码文件将会在同一目录下。
输入例子:
采集java工程师10页和python爬虫5页:java工程师,10<>python爬虫,5
在这里插入图片描述

import csv
import random
import sys
import time
import urllib.parseimport requests
from lxml import etree# 存储企业资料
enterprise_information = dict()def get_random_user_agent() -> str:"""随机取一个User-Agent返回:return: str"""user_agent = [# Opera"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60","Opera/8.0 (Windows NT 5.1; U; en)","Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50",# Firefox"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0","Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10",# Safari"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",# chrome"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11","Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16",# 360"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36","Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",# 淘宝浏览器"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",# 猎豹浏览器"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER","Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",# QQ浏览器"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E) ",# sogou浏览器"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)",# maxthon浏览器"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36",# UC浏览"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/17D50 UCBrowser/12.8.2.1268 Mobile AliApp(TUnionSDK/0.1.20.3)","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36","Mozilla/5.0 (Linux; Android 8.1.0; OPPO R11t Build/OPM1.171019.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/11.19 SP-engine/2.15.0 baiduboxapp/11.19.5.10 (Baidu; P1 8.1.0)","Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 SP-engine/2.14.0 main%2F1.0 baiduboxapp/11.18.0.16 (Baidu; P2 13.3.1) NABar/0.0 ","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.10(0x17000a21) NetType/4G Language/zh_CN","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36","Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36"]return user_agent[random.randint(0, len(user_agent) - 1)]def get_web_server_enterprise_data(enterprise_data_url: str) -> dict:"""查询招聘企业的信息:param enterprise_data_url: 访问企业资料的链接:return: dict 存储企业数据的字典"""# 判断链接的类型,有两种,show.aspx?id=420152&lx=2&Ent_SN=2765266,这种提取出ent_snif enterprise_data_url.startswith("show.aspx"):ent_sn = enterprise_data_url.split("Ent_SN=")[1]else:# /ent/2973425.htmlent_sn = enterprise_data_url.split('/ent/')[1].split(".html")[0]# 根据ens_sn查询是否以及保存过这个企业的基本信息global enterprise_information# 从字典中查询data = enterprise_information.get(ent_sn)if data is not None:# 字典中查到了这个企业的数据,直接返回了,不用发请求获取了return data# 拼接请求地址enterprise_data_url = "https://www.ynzp.com/ent/{}.html".format(ent_sn)headers = {"User-Agent": get_random_user_agent()}# 发送请求获取响应html_content = get_web_server_data(enterprise_data_url, headers, dict())# 解析出企业的资料html_element = etree.HTML(html_content)# 企业所属行业industry = html_element.xpath('//div[@id="EntBaseInfo"]/ul/li[1]/span[2]/text()')if len(industry):industry = industry[0]else:industry = "无"# 企业性质enterprise_nature = html_element.xpath('//div[@id="EntBaseInfo"]/ul/li[2]/span[2]/text()')if len(enterprise_nature):enterprise_nature = enterprise_nature[0]else:enterprise_nature = "无"# 注册资金registered_capital = html_element.xpath('//div[@id="EntBaseInfo"]/ul/li[3]/span[2]/text()')if len(registered_capital):registered_capital = registered_capital[0]else:registered_capital = "无"# 人数规模,html里有点特别size_personnel = html_element.xpath('//div[@id="EntBaseInfo"]/ul/li[4]/text()')if len(size_personnel):size_personnel = size_personnel[0]else:size_personnel = "无"# 组装数据保存并返回enterprise_data = {"industry": industry,"enterprise_nature": enterprise_nature,"registered_capital": registered_capital,"size_personnel": size_personnel}# 将数据存入字典中enterprise_data[ent_sn] = enterprise_datareturn enterprise_datadef handle_html_content(html_content: str) -> list:"""解析处理html内容:param html_content: html内容:return: list 解析出的一整页的所有岗位信息"""# 解析html内容html_element = etree.HTML(html_content)position_list = list()# 遍历每一个招聘信息for recruit_item_elem in html_element.xpath('//div[@class="V1Item clearfix "]'):# 岗位名称job_name = recruit_item_elem.xpath('./div[@class="JobName l clearfix"]/a/@title')[0]sys.stdout.write('\r' + '正在解析-->岗位名称:' + job_name)sys.stdout.flush()# 工资salary = recruit_item_elem.xpath('./div[@class="JobName l clearfix"]/div[@class="salaryStyle"]/text()')[0]# 去除两边的空格和换行salary = salary.strip()# 公司、企业名称corporate_name = recruit_item_elem.xpath('./div[@class="l ent_style1"]/a/text()')[0]# 工作地点work_location = recruit_item_elem.xpath('./div[@class="JobInfo l"]/span[1]/text()')[0]# 学历educational_qualifications = recruit_item_elem.xpath('./div[@class="JobInfo l"]/span[2]/text()')[0]# 工作经验work_experience = recruit_item_elem.xpath('./div[@class="JobInfo l"]/span[3]/text()')[0]# 工作类型work_type = recruit_item_elem.xpath('./div[@class="JobInfo l"]/span[4]/text()')[0]# 提取公司资料的url,获取公司的数据enterprise_information_url = recruit_item_elem.xpath('./div[@class="l ent_style1"]/a/@href')[0]enterprise_data = get_web_server_enterprise_data(enterprise_information_url)# 组装数据position_data = {"job_name": job_name,"salary": salary,"corporate_name": corporate_name,"work_location": work_location,"educational_qualifications": educational_qualifications,"work_experience": work_experience,"work_type": work_type,"enterprise_data": enterprise_data}# 添加到该页的总列表中position_list.append(position_data)return position_listdef get_web_server_data(url: str, headers: dict, params: dict) -> str:"""发送请求到服务器,将响应的html文本内容返回:param url: 请求的地址:param headers: 请求头:param params: 请求参数(这里都是GET请求,都是拼接到url中):return: str类型的html内容"""# 查询关键字万一有中文,先进行转码,前端他用了escape这个函数将关键字进行转码,服务器不允许直接发中文参数值if params.get("keyword"):params["keyword"] = urllib.parse.quote(params.get("keyword").encode('unicode-escape')).replace('%5Cu', '%u')# 拼接请求参数到urlurl += "?"for key in params.keys():# 逐个参数拼接到urlurl += "{}={}&".format(key, params.get(key))# 拼接到最后会多一个&,需要删除掉url = url[:-1]# 发送请求response = requests.get(url, headers=headers)# 判断服务器响应状态码是否为200 OKif response.status_code != 200:raise Exception("请求出错了!")# 设置编码,将响应编码设置为我们的目标编码,防止乱码response.encoding = response.apparent_encoding# 读取响应的html内容并返回html_context = response.textreturn html_contextdef save_data_to_csv(keyword, position_list: list) -> None:"""将岗位数据存储成csv文件:param keyword::param position_list::return:"""# 文件内容头(第一行的列名称)header = ["岗位名称", "工资", "工作地点", "学历要求", "经验要求", "工作类型","招聘企业名称", "企业行业", "企业性质", "注册资金", "人数规模"]# 获取时间,拼接在文件名中lt = time.localtime()t = "{}月{}日{}点{}分".format(lt.tm_mon, lt.tm_mday, lt.tm_hour, lt.tm_min)file_name = "./{}-{}.csv".format(keyword, t)with open(file_name, "w+", encoding="utf-8") as f:csv_file = csv.writer(f)# 先写入第一行的列名称csv_file.writerow(header)for position_dict in position_list:row = list()# 调整一下每一行写入的数据的顺序# 岗位名称row.append(position_dict["job_name"])# 工资row.append(position_dict["salary"])# 工作地点row.append(position_dict["work_location"])# 学历要去row.append(position_dict["educational_qualifications"])# 经验要求row.append(position_dict["work_experience"])# 工作类型row.append(position_dict["work_type"])# 招聘企业名称row.append(position_dict["corporate_name"])# 企业行业row.append(position_dict["enterprise_data"]["industry"])# 企业性质row.append(position_dict["enterprise_data"]["enterprise_nature"])# 注册资金row.append(position_dict["enterprise_data"]["registered_capital"])# 人数规模row.append(position_dict["enterprise_data"]["size_personnel"])# 将列表写成文件中的一行csv数据csv_file.writerow(row)# 打印sys.stdout.write('\r' + '将关键字:{}\t查询到的数据写入文件:{}\t完毕!'.format(keyword, file_name))sys.stdout.flush()def collect(keyword: str, collect_page_number: int) -> None:"""采集流程的主要控制函数:param keyword: 搜索关键字:param collect_page_number: 要采集的页码数:return: None"""# 搜索的请求链接url = "https://www.ynzp.com/search/offer_search_result.aspx"# 模拟浏览器的请求头,防止被识别为爬虫headers = {"Host": "www.ynzp.com",# 随机用一个User-Agent"User-Agent": get_random_user_agent()}# 请求参数request_params = {# 搜索关键字"keyword": keyword,"jcity1Hidden": "330000","sex": "undefined","expr": -1,"SortWay": 2,"areatitle": "","lat": 0,"lng": 0,"zoom": 0,"ma": 0,# 默认页码是1"page": 1}# 改关键字的最大页码数last_search_result_page_num = collect_page_number# 该搜索关键字的所有岗位列表all_result_position_list = list()# 每一页for page_number in range(1, collect_page_number + 1):# 更新请求携带的页码request_params["page"] = page_number# 调用函数,发送请求得到数据html_content = get_web_server_data(url, headers, request_params)# 如果是第一页,那就提取一下这个关键字搜索到的最大页数if page_number == 1:# 使用etree解析html内容html_element = etree.HTML(html_content)# ['offer_search_result.aspx?keyword=java&jcity1Hidden=330000&sex=undefined&expr=-1&SortWay=2&areatitle=&lat=0&lng=0&zoom=0&ma=0&page=14']# last_page_href = html_element.xpath('//div[@id="ctl00_ContentPlaceHolder1_AspNetPager1"]/span[8]/a/@href')[#     0]page_spans = html_element.xpath('//div[@id="ctl00_ContentPlaceHolder1_AspNetPager1"]/span')# 当搜索结果不满第一页时,不会显示分页这些的if len(page_spans):last_page_span = page_spans[-1]last_page_href = last_page_span.xpath('./a/@href')[0]# 分割出最后一页是多少last_page = last_page_href.split("page=")[1]else:last_page = 1# 记录一下last_search_result_page_num = int(last_page)# 处理服务器响应的数据now_page_position_list = handle_html_content(html_content)# 将当前页的岗位列表信息添加到整个关键字搜索结果岗位列表all_result_position_list += now_page_position_listif page_number >= last_search_result_page_num:# 页码数大于搜索结果尾页,则结束这个关键字的采集break# 采集完所有页码的数据,保存到文件,传入关键字做为文件名称开头sys.stdout.write('\r' + '采集关键字【{}】结束,正在将数据存入csv文件'.format(keyword))sys.stdout.flush()save_data_to_csv(keyword, all_result_position_list)def main():collect_condition = input("请输入才采集的关键字和采集页码数(每页有40条招聘信息,最高50页),用<>分割(例:java开发,10<>销售,15):")sys.stdout.write('\r' + '程序开始运行')sys.stdout.flush()# 分割出每个采集的关键字for i in collect_condition.split("<>"):# 分割出关键字名称和这个岗位的采集数量i = i.split(",")keyword = i[0]collect_page_number = int(i[1])# 服务器限制了一个关键字最高查询到50页的数据(2000条)if collect_page_number > 50: collect_page_number = 50# 逐个对每个关键字进行采集collect(keyword, collect_page_number)# 打印sys.stdout.write('\r' + '程序执行结束!')sys.stdout.flush()if __name__ == '__main__':main()

教程和代码仅供学习交流,请勿用于其他非法用途!

欢迎加入python学习交流QQ群:891938703

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/163664.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java基础面试二十四】、String类有哪些方法?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;String类有哪些方法&…

10.17课上(七段显示器,递归异或与电路)

异或的递归与数电实现 用二选一选择器实现异或函数 在异或当中&#xff0c;如果有一项为0&#xff0c;就可以把那一项消掉&#xff1b;如果有一项为1&#xff0c;就是把剩下的所有项运算完的结果取反 &#xff08;由此在算法当中可以采用递归解决&#xff09; 当w1为0时&…

CleanMyMac苹果电脑清理软件是智商税吗?最全评测价格、清理效果一次说清

这是一篇CleanMyMac最全评测&#xff01;价格、清理效果一次说清&#xff0c;告诉你它真不是智商税! 升级Ventura系统之前&#xff0c;我用的是CleanMyMac X绿色版&#xff08;绝不提倡这个行为&#xff09;。更新到Ventura之后&#xff0c;之前很多绿色软件失效&#xff0c;浪…

TP5.1 导出excel文件

在 ThinkPHP 5.1 中引入 PHPExcel&#xff08;现在已被官方弃用&#xff0c;推荐使用 PhpSpreadsheet&#xff09;时&#xff0c;可以按照以下步骤进行操作&#xff1a; 在 composer.json 文件中添加 PHPExcel&#xff08;PhpSpreadsheet&#xff09;的依赖项。找到 require 部…

Pygame中实现图片的移动

在《Pygame中将鼠标形状设置为图片2-1》和《Pygame中将鼠标形状设置为图片2-2》中提到将鼠标设置为指定图片。接下来在该文章涉及到的代码基础之上&#xff0c;实现图片的移动&#xff0c;效果如图1所示。 图1 图片移动效果 从图1中可以看出&#xff0c;导入一个大猩猩的图片&…

DayDreamInGIS 逆地理编码工具(根据经纬度获取位置描述)插件源码解析

本工具调用高德地图逆地理编码api&#xff0c;根据高的地图逆地理编码api&#xff0c;实现根据经纬度获取位置描述。 总体设计逻辑&#xff0c;窗体采用WPF&#xff0c;通过属性的方式传递交互对象&#xff0c;核心处理逻辑写到button的执行逻辑中。 1.页面 页面XAML&#xf…

掌握JavaScript的练习之道:十个手写函数让你信手拈来!

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

MySQL事务MVCC详解

一、概述 MVCC (MultiVersion Concurrency Control) 叫做多版本并发控制机制。主要是通过数据多版本来实现读-写分离&#xff0c;做到即使有读写冲突时&#xff0c;也能做到不加锁&#xff0c;非阻塞并发读&#xff0c;从而提高数据库并发性能。 MVCC只在已提交读&#xff08…

行业追踪,2023-10-18

自动复盘 2023-10-18 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

Docker是一个流行的容器化平台,用于构建、部署和运行应用程序。

文章目录 Web应用程序数据库服务器微服务应用开发环境持续集成和持续部署 (CI/CD)应用程序依赖项云原生应用程序研究和教育 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;…

mysql查看连接池的命令

查看实时连接的个数 &#xff08;瞬时值&#xff09; SHOW STATUS LIKE Threads_connected; 查看具体的链接信息 show full processlist; 数据库链接池常见的报错 Cannot create PoolableConnectionFactory (Data source rejected establishment of connection, message fr…

Android Fragment 基本概念和基本使用

Android Fragment 基本概念和基本使用 一、基本概念 Fragment&#xff0c;简称碎片&#xff0c;是Android 3.0&#xff08;API 11&#xff09;提出的&#xff0c;为了兼容低版本&#xff0c;support-v4库中也开发了一套Fragment API&#xff0c;最低兼容Android 1.6。 过去s…

Swift使用Embassy库进行数据采集:热点新闻自动生成器

概述 爬虫程序是一种可以自动从网页上抓取数据的软件。爬虫程序可以用于各种目的&#xff0c;例如搜索引擎、数据分析、内容聚合等。本文将介绍如何使用Swift语言和Embassy库编写一个简单的爬虫程序&#xff0c;该程序可以从新闻网站上采集热点信息&#xff0c;并生成一个简单…

Redis的五大基础数据类型

String 字符串类型&#xff0c;通过set关键字和get关键字来设置字符串键值对和获取字符串键值对。 hash 哈希类型&#xff0c;结构和Map<String,Map<String,stirng>>类似。 使用hset来设置哈希&#xff0c;使用hget来获取哈希&#xff0c;hget要精确到第二个key…

苍穹外卖(八) 使用WebSocket协议完成来单提醒及客户催单功能

WebSocket介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信(双向传输)——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c; 并进行双向数据传输。 HTTP协议和WebSocket协议对比&#xff1a; HTTP…

2022年下半年 软件设计师 上午试卷(前21题)

以下关于RISC&#xff08;精简指令集计算机&#xff09;特点的叙述中&#xff0c;错误的是 &#xff08;1&#xff09; 。 &#xff08;1&#xff09; A. 对存储器操作进行限制&#xff0c;使控制简单化 B. 指令种类多&#xff0c;指令功能强 C. 设置大量通用寄存器 D. 选…

【算法|动态规划No.23】leetcode376. 摆动序列

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【C#】【winform】Microsoft Visual Studio Installer Project 打包应用程序全部过程

提示&#xff1a;只针对扩展包来完成打包的工作过程。 文章目录 前言一、Microsoft Visual Studio Installer Project 是什么&#xff1f;二、安装1.安装Microsoft Visual Studio Installer Project 三、安开始打包1.添加setup1.在解决方案上面右键&#xff0c;添加-新建项目2.…

H5+Vue3编写官网,并打包发布到同一个域名下

背景 因为html5有利于搜索引擎抓取和收录我们网站更多的内容&#xff0c;对SEO很友好&#xff0c;可以为网站带来更多的流量,并且多端适配&#xff0c;兼容性和性能都非常不错&#xff0c;所以使用h5来编写官网首页。 因为用户个人中心可以通过官网跳转&#xff0c;不需要被浏…

双目视觉实战--单视图测量方法

目录 一.简介 二、2D变换 1. 等距变换&#xff08;欧式变换&#xff09; 2. 相似变换 3. 仿射变换 4. 射影变换&#xff08;透视变换&#xff09; 5. 结论 三、影消点与影消线 1. 平面上的线 2. 直线的交点 3. 2D无穷远点 4. 无穷远直线 5. 无穷远点的透视变换与仿…