def parseContactsTable(self, table): def convertDaysCountKV(texts): result = [] for text in texts: # 5月1日など月が記載されている箇所があるため、月以前を削除する if re.search(r'月', text) is not None: _, text = re.split(r'月', text) # 誤字で日が件と表示されている箇所があるため、日と件の両方で分割 key, count, *_ = re.split(r'日|件', text) result.append({'day': int(key), 'count': int(count)}) return result items = table.findAll('tr')[1:] res_dict = [] for item in items: # span # td属性からテキストを取得 span, sum_number_tmp, individuals_tmp = list( map(self.get_text_rm_p_tag, item.findAll('td'))) individuals = list(individuals_tmp.split('、')) # spanからreturnするdictonaryのlistを取得する target_list = TimeUtil().get_dt_dict_from_text(span) # 日ごとの件数をkvで取得 days_count_dict = convertDaysCountKV(individuals) for day_count in days_count_dict: target_list = list(map(lambda x: { '日付': x['日付'], '小計': day_count['count'] if x['day'] == day_count['day'] else x['小計'], 'day': x['day']}, target_list)) res_dict.extend(target_list) # 一次変数の'day'を消して返却 return list(map(lambda x: {'日付': x['日付'], '小計': x['小計']}, res_dict))
def create_new_patient_dict(self, patient_soup, scr): # 対象の階層のデータだけ抽出する data = scr.find_h4(patient_soup) patients = [] patient = {} patient["No"] = StringUtil.exclude_info_number(data.text) # 県外事例は除外 inside_checker = StringUtil() for index, sibling in enumerate(data.next_siblings): # if index % 2 != 0: # 改行コードはスキップする if hasattr(sibling, "text"): target = sibling.text else: target = sibling # h4属性の場合、新たなpatient dictを作成する if sibling.name == 'h4': # 患者情報でない余計な情報の場合、dictへの格納を行わない if len(patient.keys()) > 1 and inside_checker.exclude_outside(patient["No"]): patient["退院"] = None if patient["発生判明日"] is not None: patient["発生判明日"] = TimeUtil().convert_wareki_to_ad( patient["発生判明日"]) patients.append(self.sort_patients_dict(patient)) patient = {} patient["No"] = StringUtil.exclude_info_number(target) continue # 年代|性別|発生判明日|居住地の情報をセットする flg, key, value = StringUtil.set_key_value(target) if flg and key not in patient: patient[key] = value # h2タグが表示された時点で別と患者情報の表示は終了する if sibling.name == 'h2': patient["退院"] = None if patient["発生判明日"] is not None: patient["発生判明日"] = TimeUtil().convert_wareki_to_ad( patient["発生判明日"]) patients.append(self.sort_patients_dict(patient)) break print(patients) self.patient_list = list(filter(lambda x: re.search( r'\d', x['No']) is not None, patients))
def create_patients_summary_dict(self, patient_list): # patients_summaryの作成 patients_summary = TimeUtil().create_dt_dict(datetime.datetime.now()) # patient_listから"発生判明日"のみのリストを作成 patients_summary_tmp = list(map(lambda x: x["発生判明日"], patient_list)) for k, g in groupby(patients_summary_tmp): patients_summary = list(map(lambda x: {"日付": x["日付"], "小計": len( list(g)) if x['日付'] == k else x['小計']}, patients_summary)) # 小計が0とならない最新の日付までのリストにする patients_summary = sorted(patients_summary, key=lambda x: x['日付']) patients_summary = JsonChecker.exclude_zero_max_date(patients_summary) self.patients_summary_data['data'] = patients_summary
def getPatientDict(index_html, scr, update_datetime): # 発生状況等の取得 url = scr.getTargetUrl(index_html, 'info_coronavirus_prevention.html') soup = scr.getContent(url) # テーブル情報を取得する try: dataset = scr.parseSingleTable(soup) except ValueError as e: raise e # pdf格納folderの作成 path_op = PathOperator() path_op.create_path('pdf') # 県外事例は除外 inside_checker = StringUtil() patient_data_tmp = [] patients_summary_tmp = [] for data in dataset: if inside_checker.exclude_outside(data[0]): # 格納先のファイル名を作成 file_name = path_op.set_downlaod_file_name( 'pdf', path_op.get_file_name(data[3])) scr.downloadPdf(data[3], file_name) output_file = os.path.splitext(os.path.basename(file_name))[0] output_path = os.path.join('./text', output_file + '.txt') is_duplicated, number_char = StringUtil().is_duplicate_data( data[0]) # 重複している場合 if is_duplicated: for n in number_char: # print('No:{} リリース日:{} 判明日:{} Link:{}'.format(n, data[1].strip(), data[2].strip(), output_path)) patient_data_tmp.append({ "No": n, "リリース日": data[1].strip(), "link": output_path }) patients_summary_tmp.append(data[2].strip()) # 単一の場合 else: # print('No:{} リリース日:{} 判明日:{} Link:{}'.format(number_char, data[1].strip(), data[2].strip(), output_path)) patient_data_tmp.append({ "No": number_char, "リリース日": data[1].strip(), "link": output_path }) patients_summary_tmp.append(data[2].strip()) # convertの実行 path_op.create_path('text') convert_txt = PdfParser() convert_txt.executeConvert() # テキストからjsonの作成 parser = TextParser() patient_list = [] # patientの作成 for tmp in patient_data_tmp: result = parser.text2dict(tmp['No'], tmp['link']) result.update(tmp) result.update({'退院': None}) del result['link'] patient_list.append(result) patients = {} patients['__comments'] = "陽性患者の属性" patients['date'] = update_datetime # Noに数字が入らない場合の処理(例:"再陽性") nan_patient_list = list( filter(lambda x: re.search(r'\d', x['No']) is None, patient_list)) number_patient_list = list( filter(lambda x: re.search(r'\d', x['No']) is not None, patient_list)) insert_patient_list = sorted( number_patient_list, key=lambda x: int(re.sub(r'県|内|例|目', '', x['No']))) insert_patient_list.extend(nan_patient_list) patients['data'] = insert_patient_list # patients_summaryの作成 patients_summary_data = {} patients_summary_data['__comments'] = "陽性患者数" patients_summary_data['date'] = update_datetime patients_summary = TimeUtil().create_dt_dict(datetime.datetime.now()) for k, g in groupby(patients_summary_tmp): patients_summary = list( map( lambda x: { "日付": x["日付"], "小計": len(list(g)) if x['日付'] == k else x['小計'] }, patients_summary)) # 小計が0とならない最新の日付までのリストにする patients_summary = sorted(patients_summary, key=lambda x: x['日付']) jc = JsonChecker() patients_summary = jc.exclude_zero_max_date(patients_summary) patients_summary_data['data'] = patients_summary return patients, patients_summary_data
def tmu_object(): tmu = TimeUtil() yield tmu
def __init__(self, base): self.base = base self.time_util = TimeUtil()
class Scraper: def __init__(self, base): self.base = base self.time_util = TimeUtil() def getTargetUrl(self, base_url: str, target_url: str): r = requests.get(self.base + base_url) r.encoding = r.apparent_encoding soup = BeautifulSoup(r.text, 'html.parser') elems = soup.find(href=re.compile(target_url)) return elems.get('href') def getContent(self, url: str): r = requests.get(self.base + url) r.encoding = r.apparent_encoding return BeautifulSoup(r.text, 'html.parser') def downloadPdf(self, pdf_url: str, path_name: str) -> None: r = requests.get(self.base + pdf_url, stream=True) with open(path_name, 'wb') as fd: for chunk in r.iter_content(chunk_size=2000): fd.write(chunk) def parseSingleTable(self, soup_data): table = soup_data.findAll('table')[0] numbers = table.findAll("th")[1:] rows = table.findAll("tr")[1:] dataset = [] for number, row in zip(numbers, rows): day_proved, tmp = row.findAll("td") # dayにpタグが付いているパターンがある correct_date = tmp.findAll('a')[-1] day_release = correct_date.getText() try: ad_day_release = self.time_util.convert_wareki_to_ad( day_release.strip()) ad_day_proved = self.time_util.convert_wareki_to_ad( day_proved.text.strip()) except ValueError as e: raise e link = correct_date.get('href') dataset.append( [number.find('p').text, ad_day_release, ad_day_proved, link]) return dataset def findAllTable(self, soup_data): return soup_data.findAll('table') def find_p_and_h4(self, soup_data): return soup_data.findAll(['p', 'h4']) def find_h4(self, soup_data): return soup_data.find('h4') def get_text_rm_p_tag(self, soup): if len(soup.findAll('p')) != 0: return soup.findAll('p')[0].text.strip() else: return soup.text.strip() def parseContactsTable(self, table): def convertDaysCountKV(texts): result = [] for text in texts: # 5月1日など月が記載されている箇所があるため、月以前を削除する if re.search(r'月', text) is not None: _, text = re.split(r'月', text) # 誤字で日が件と表示されている箇所があるため、日と件の両方で分割 key, count, *_ = re.split(r'日|件', text) result.append({'day': int(key), 'count': int(count)}) return result items = table.findAll('tr')[1:] res_dict = [] for item in items: # span # td属性からテキストを取得 span, sum_number_tmp, individuals_tmp = list( map(self.get_text_rm_p_tag, item.findAll('td'))) individuals = list(individuals_tmp.split('、')) # spanからreturnするdictonaryのlistを取得する target_list = TimeUtil().get_dt_dict_from_text(span) # 日ごとの件数をkvで取得 days_count_dict = convertDaysCountKV(individuals) for day_count in days_count_dict: target_list = list( map( lambda x: { '日付': x['日付'], '小計': day_count['count'] if x['day'] == day_count['day'] else x['小計'], 'day': x['day'] }, target_list)) res_dict.extend(target_list) # 一次変数の'day'を消して返却 return list(map(lambda x: {'日付': x['日付'], '小計': x['小計']}, res_dict))