def get_plots(self, city_name, city_url): '''采集当前城市小区基本数据 访问当前城市的小区列表 在第一页获取小区数量, 然后判断出最大页数 循环页码, 组合小区列表页 url 访问并将 html 转换为 etree 对象, 并获取小区相关数据标签 etree 对象 采集小区列表中的 a 标签, 其中有小区名称和小区详情信息url 循环结束, 当前城市小区基本数据采集结束, 保存至 JSON 文件中 Args: city_name: 城市名 city_url: 城市小区列表url ''' page, max_page, plots_dict = 1, 1, dict() while page <= max_page: temp_url = city_url.format(page=page) common.print_and_sleep('吉屋网:采集{city_name}小区第{page}页: {url}'.format( city_name=city_name, page=page, url=temp_url), time_interval=self.time_interval) etree = gather.get_html_to_etree(temp_url, headers=self.headers) if page == 1: max_page = math.ceil( int(etree.xpath('//div[@class="txt"]/span/text()')[0]) / 20) for plot_etree in etree.xpath('//div[@class="tit"]/a'): plot_name, plot_url = plot_etree.xpath( 'text()')[0], plot_etree.xpath('@href')[0] plots_dict[plot_name] = { 'plot_name': plot_name, 'plot_url': plot_url } page += 1 print('{city_name}小区采集完成,保存数据'.format(city_name=city_name)) operation_file.write_json_file( self.plots_file_name.format(city_name=city_name), plots_dict)
def _get_plot_detail(self, plot_dict): '''获取当前小区的详细信息 访问小区的详情URL, 并获取 HTML 并转换为 etree 对象 采集地址, 经纬度以及其他信息 将采集的信息整合到旧字典中并返回 Args: plot_dict: 小区的当前信息数据, 包含小区名和小区详情URL return: dict 小区的最新信息数据 ''' common.print_and_sleep('吉屋网: 采集{plot}详情: {url}'.format( plot=plot_dict['plot_name'], url=plot_dict['plot_url']), time_interval=self.time_interval) etree = gather.get_html_to_etree(plot_dict['plot_url'], headers=self.headers) try: # 测试时, 一些小区回莫名其妙的报错, 并且在下次执行时又能正常执行, 所以报错时此小区跳过, 等下次执行时再次尝试 plot_dict['经纬度'] = etree.xpath('//input[@id="lng"]/@value')[ 0] + ',' + etree.xpath('//input[@id="lat"]/@value')[0] except BaseException: return plot_dict plot_dict['地址'] = etree.xpath('//input[@id="address"]/@value')[0] plot_dict['城市'] = etree.xpath('//input[@id="cityName"]/@value')[0] plot_dict['区域'] = etree.xpath('//input[@id="areaName"]/@value')[0] other_details = etree.xpath('//div[@class="bottom xqxx"]/ul/li') for other_detail in other_details: try: # 有值为空白的情况, 当为空白时, 获取的数据为 [], 会超出最大索引 key, value = other_detail.xpath( 'span/text()')[0][:-1], other_detail.xpath('em/text()')[0] plot_dict[key] = value except BaseException: pass return plot_dict
def get_plots(self, city_name, city_url): '''采集当前城市小区基本数据 访问当前城市的小区列表(第一页, 但是 url 中无分页标识, 否则获取的区域 url 就会有分页标识) 判断一下 class 为 sec-list-nav clearfix 的 div 是否存在, 如果存在则说明当前城市没有小区, 直接返回 获取城市区域的名称及 url 循环城市区域, 循环页码, 组合小区列表页 url 访问并将 html 转换为 etree 对象, 并获取小区相关数据标签 etree 对象 判断当前区域当前页码中是否有小区数据, 如果没有则说明本区域小区数据采集完毕 若有小区数据则将小区数据暂存在字典中 所有循环结束, 当前城市小区基本数据采集结束, 保存至 JSON 文件中 ''' page_maximum, page, plot_dict = 1, 1, dict() while page <= page_maximum: plot_url = self.plots_url.format(domain=city_url, page=page) common.print_and_sleep('采集{city}第{page}页: {url}'.format( city=city_name, page=page, url=plot_url)) plot_etree = gather.get_html_to_etree(plot_url, headers=self.headers) if page_maximum == 1 and page == 1: try: page_maximum = int( plot_etree.xpath(self.page_maximum_xpath)[0]) except BaseException: page_maximum = 1 for plot_etree in plot_etree.xpath(self.plots_xpath): plot_name, plot_url = plot_etree.xpath( 'text()')[0], plot_etree.xpath('@href')[0] plot_dict[plot_name] = { 'plot_name': plot_name, 'plot_url': city_url + '/' + plot_url } page += 1 print('{city_name}采集结束,保存数据'.format(city_name=city_name)) operation_file.write_json_file( self.plots_file_name.format(city=city_name), plot_dict)
def get_plots(self, city_name, city_url, plots_url): '''获取小区列表 从第一页开始循环 处理小区列表 URL(城市列表中的 URL 并非小区列表的 URL), 并访问, 然后将 HTML 转换为 etree 对象 判断一个 class 为 zhuan 的 div 标签是否存在, 如果存在则说明此页没有数据, 也说明当前城市数据采集完毕, 终止循环 获取小区列表中每个小区的名称及其 url, 并暂存到字典中, 在循环结束是保存到 JSON 文件中 ''' page, plots_dict = 1, dict() while True: temp_plots_url = plots_url.format(page=page) common.print_and_sleep('{city_name}城市小区采集第{page}页: {url}'.format( city_name=city_name, page=page, url=temp_plots_url)) plots_etree = gather.get_html_to_etree(temp_plots_url, headers=self.headers) if len(plots_etree.xpath(self.plots_error_xpath)) >= 1: break for plot_etree in plots_etree.xpath(self.plots_xpath): plot_name, plot_url = plot_etree.xpath( 'text()')[0], plot_etree.xpath('@href')[0] plots_dict[plot_name] = { 'plot_name': plot_name, 'plot_url': city_url + plot_url } page += 1 plots_file_name = self.plots_file_name.format(city_name=city_name) common.print_and_sleep('{city_name}城市小区采集结束,保存数据至{file_name}'.format( city_name=city_name, file_name=plots_file_name)) operation_file.write_json_file(plots_file_name, plots_dict)
def _get_plot_detail(self, plot_dict): '''获取小区的详细数据 访问小区字典中的小区详情 URL, 并将得到的 HTML 转换为 etree 对象 获取所有详情数据, 地址, 经纬度等等 Args: plot_dict: 小区信息字典, 当前仅有小区名称及小区详情页URL return: dict, 小区完整信息字典 ''' common.print_and_sleep('采集{name}小区详情:{url}'.format( name=plot_dict['plot_name'], url=plot_dict['plot_url'])) etree = gather.get_html_to_etree(plot_dict['plot_url'], headers=self.headers) try: plot_dict['地址'] = etree.xpath(self.plot_detail_address_xpath)[0] except BaseException: plot_dict['地址'] = '' return plot_dict plot_dict['地址链'] = '>'.join(etree.xpath(self.plot_detail_area_xpath)) try: plot_dict['经纬度'] = etree.xpath(self.plot_detail_nautica_xpath)[0] except IndexError: plot_dict['经纬度'] = '' plot_dict.update( zip(etree.xpath(self.plot_detail_other_keys_xpath), etree.xpath(self.plot_detail_other_values_xpath))) return plot_dict
def get_citys(self): '''获取城市列表 访问城市列表 URL, 并将 HTML 转换为 etree 对象 获取城市信息的 a 标签, 并从 a 标签中拿出城市名和城市 URL, 将这些数据保存到字典类型的变量中 将数据保存到 JSON 文件中 ''' common.print_and_sleep('采集搜楼啦城市信息: {url}'.format(url=self.citys_url)) citys_etree = gather.get_html_to_etree(self.citys_url, headers=self.headers) citys_dict = dict() for city_etree in citys_etree.xpath(self.citys_xpath): city_name, city_url = city_etree.xpath( 'text()')[0], city_etree.xpath('@href')[0] citys_dict[city_name] = { 'city_name': city_name, 'city_url': city_url } operation_file.write_json_file(self.citys_file_name, citys_dict)
def _get_plots(self, city_name, city_url): '''获取城市中的小区 初始化当前页码, 最大页码, 使其能通过循环的判定, 以进行第一次循环 将当前页码与 URL 组合, 访问此 URL 并将 HTMl 转换为 etree 对象 第一次循环, 需要采集到最大页码 (此处如果报错, 说明当前城市无小区信息或者被服务端检测到并拦截了) 从 etree 对象中得到小区信息所在的 a 标签 从 a 标签中取出小区名和小区对应的详情信息 URL 当前城市小区采集结束后, 保存数据至 JSON 文件 Args: city_name: 城市名称 city_url: 城市对应的首页地址(非城市的小区地址) return: none ''' page_maximum, page, plot_dict = 1, 1, dict() while page <= page_maximum: plot_url = city_url.format(page=page) common.print_and_sleep('开始采集:{url}'.format(url=plot_url)) plot_etree = gather.get_html_to_etree(plot_url, headers=self.headers) # 获取最大页码 if page_maximum == 1 and page == 1: try: page_maximum = math.ceil( int(plot_etree.xpath(self.page_maximum_xpath)[0]) / 30) except BaseException: print('获取最大页码异常, 跳过当前城市: {city_name}'.format( city_name=city_name)) return # 获取小区名称及url for plot_etree in plot_etree.xpath(self.plot_list_xpath): plot_name, plot_url = plot_etree.xpath( 'text()')[0], plot_etree.xpath('@href')[0] plot_dict[plot_name] = { 'plot_name': plot_name, 'plot_url': plot_url } # 下次循环的准备 page += 1 # 将数据保存至文件中 common.print_and_sleep( '{city_name}采集结束,保存数据'.format(city_name=city_name)) operation_file.write_json_file( self.plots_file_name.format(file_name=city_name), plot_dict)
def get_citys(self): '''获取城市列表 访问城市列表 URL, 并将 HTML 转换为 etree 对象 获取城市信息的 a 标签, 并从 a 标签中拿出城市名和城市 URL, 将这些数据保存到字典类型的变量中 将数据保存到 JSON 文件中 ''' common.print_and_sleep('吉屋网:采集城市列表', time_interval=self.time_interval) etree = gather.get_html_to_etree(self.citys_url, headers=self.headers) citys_etree = etree.xpath('//div[@class="fivindexcont citymar"]//a') citys_dict = dict() for city_etree in citys_etree: city_name, city_url = city_etree.xpath( 'text()')[0], self.plots_url.format( city_url=city_etree.xpath('@href')[0], page="{page}") citys_dict[city_name] = { 'city_name': city_name, 'city_url': city_url } operation_file.write_json_file(self.citys_file_name, citys_dict)
def get_plots(self, city_name, city_url): '''获取小区列表 处理小区列表 URL(城市列表中的 URL 并非小区列表的 URL) 每个城市的小区列表有两个变量, 一个是区域(包含城市自身和周边辖区, 从1开始按数字排序), 另一个是页码 一层循环, 循环区域num, 当前区域的第一页小区列表在此获取, 可以判定当前区域num中是否有小区列表, 如果没有则说明是无效区域, 则当前城市小区采集结束 二层循环, 循环页码, 从页码2开始采集(页码1已经采集过了), 同样根据是否有小区列表来判定此区域是否采集完成 将数据保存到 JSON 文件中 ''' plots_url = self.plots_url.format(city_url=city_url, area_num="{area_num}", page="{page}") area_num, plots_dict = 1, dict() while True: temp_plots_url = plots_url.format(area_num=area_num, page=1) common.print_and_sleep( '采集{city_name}城市第{area_num}区域第{page}页: {url}'.format( city_name=city_name, area_num=area_num, page=1, url=temp_plots_url)) plots_dict, is_exist = self._get_plots(plots_dict, temp_plots_url) if is_exist is False: break page = 2 while True: temp_plots_url = plots_url.format(area_num=area_num, page=page) common.print_and_sleep( '采集{city_name}城市第{area_num}区域第{page}页: {url}'.format( city_name=city_name, area_num=area_num, page=page, url=temp_plots_url)) plots_dict, is_exist = self._get_plots(plots_dict, temp_plots_url) if is_exist is False: break page += 1 area_num += 1 operation_file.write_json_file( self.plots_file_name.format(city_name=city_name), plots_dict)
def get_plots_detail(self, file_name): '''获取当前城市的小区详细信息 读取小区信息文件, 并循环每个小区信息 判断此小区是否已采集过详细信息(这里按`地址`这个键来判断), 没有采集则采集详细信息 每采集20个小区的详细信息则保存一次数据, 小区采集结束后也会保存一次数据 Args: file_name: 单城市的小区信息文件 ''' plots_dict = operation_file.read_json_file(file_name) i = 1 for key in plots_dict.keys(): if plots_dict[key].get('地址') is None: common.print_and_sleep('采集{name}小区详情: {url}'.format( name=key, url=plots_dict[key]['plot_url'])) plots_dict[key] = self._get_plot_detail(plots_dict[key]) i += 1 if i % config.save_file_number == 0: print('更新文件:{file_name}'.format(file_name=file_name)) operation_file.write_json_file(file_name, plots_dict) if i > 1: print('更新文件:{file_name}'.format(file_name=file_name)) operation_file.write_json_file(file_name, plots_dict)
def get_plots_detail(self, plots_file_name): '''获取文件中的小区详情数据 读取小区数据文件中的数据, 遍历 判断当前小区数据中是否存在`地址`数据, 如果存在则已经获取过详细数据了, 如果没有则获取 没采集一定数量的小区详情数据, 就更新一次文件(防止因特殊情况中断采集, 导致采集的数据全部丢失) 循环结束, 更新文件 ''' plots_dict = operation_file.read_json_file(plots_file_name) i = 1 for plot_key in plots_dict.keys(): if plots_dict[plot_key].get('经纬度') is None: common.print_and_sleep('采集{name}小区详情: {url}'.format( name=plots_dict[plot_key]['plot_name'], url=plots_dict[plot_key]['plot_url'])) plots_dict[plot_key] = self._get_plot_detail( plots_dict[plot_key], plots_dict[plot_key]['plot_url']) i += 1 if i % config.save_file_number == 0: print( '更新文件: {file_name}'.format(file_name=plots_file_name)) operation_file.write_json_file(plots_file_name, plots_dict) if i > 1: print('更新文件: {file_name}'.format(file_name=plots_file_name)) operation_file.write_json_file(plots_file_name, plots_dict)