Ejemplo n.º 1
0
def parse_cite_list(soup: BeautifulSoup, uid: str):
    logger = Logger()  # 로기
    link = ''  # 추가 연도별 tc data가 있는 페이지 링크
    tc_data = {}  # 연도별 tc data
    # tc_paper_data   = []

    # TAG 정의 #

    TAG = {
        'PUB_YEAR_LINK': 'a#PublicationYear',
        'PUB_YEARS': 'div#PublicationYear_tr'
    }

    # TAG 정의 끝 #

    # 예외 페이지 처리 #
    if (re.search(r'None of the Citing Articles are in your subscription',
                  soup.text, re.I)):
        raise exceptions.CiteListNoSubsError()
    # 예외 페이지 처리 끝 #

    # FAST5000 데이터 다운로드 #
    # 현재는 생략한다.
    # FAST5000 데이터 다운로드 끝#

    # 더 많은 연도를 조회할 필요가 있는 지 확인 #
    logger.log('info', 'CITE_LIST//[%s] 더 많은 연도를 조회할 필요가 있는 지 확인' % uid)
    pub_year_link = soup.select_one(TAG['PUB_YEAR_LINK'])

    if pub_year_link:
        link = pub_year_link['href']
        return link, tc_data
    # 더 많은 연도를 조회할 필요가 있는 지 확인 끝 #

    pub_year_div = soup.select_one(TAG['PUB_YEARS'])

    regex_cnt_by_year = r'([1-9][0-9]{3}) \(([0-9]+)\)'
    ms = re.findall(regex_cnt_by_year, pub_year_div.text)

    for tub in ms:
        tc_data[tub[0]] = int(tub[1])

    logger.log('info', 'CITE_LIST//[%s] DONE' % uid)
    return link, tc_data
Ejemplo n.º 2
0
def parse_tc_data(soup: BeautifulSoup, uid: str):
    logger = Logger()  # 로거
    tc_data = {}  # 연도별 tc data

    # TAG 정의 #

    TAG = {
        'TC_DATA_TR': 'tr#PublicationYear_raMore_tr',
    }

    # TAG 정의 끝 #

    # TC DATA 파싱 #
    logger.log('info', 'TC_DATA//[%s] TC DATA 파싱' % uid)
    raw_tc_data = soup.select_one(TAG['TC_DATA_TR'])

    regex_cnt_by_year = r'([1-9][0-9]{3}) \(([0-9]+)\)'
    ms = re.findall(regex_cnt_by_year, raw_tc_data.text)

    for tub in ms:
        tc_data[tub[0]] = int(tub[1])

    logger.log('info', 'TC_DATA//[%s] DONE' % uid)
    return tc_data
Ejemplo n.º 3
0
def cons_callback(ch, method, properties, body):
    logger = Logger()
    '''
        바디 메세지 형식 : "type, uid, targetURL, extra"

        - type      : 페이지 타입 (DETAIL, CITE_CNT)
        - uid       : WOS 레코드 uid
        - targetURL : 목적 파싱 주소
        - extra     : 기타 인자
    '''
    ch.basic_ack(delivery_tag=method.delivery_tag)

    # 로직 시작 #
    try:
        ## 파라미터 추출 ##
        str_body: str = str(body.decode('utf-8'))
        args: dict = json.loads(str_body)

        targetType = args['sourceType']
        uid = args['UID']
        targetURL = args['targetURL']
        # extra       = args['extra']

        mailman = Mailman()
        parser = ParserInterface(mailman)

        ## 파라미터 추출 끝 ##
        logger.log('info', 'Message received "%s" ' % (', '.join(args)[:50]))
        logger.log('info', 'URL: (%s)' % (targetURL))

        ## 파싱 쓰레드 준비
        x = threading.Thread(target=parser.run,
                             args=(
                                 targetType,
                                 uid,
                                 targetURL,
                             ))

        ## 과잉 쓰레딩 방어 (threading.enumerate()은 다른 스레드도 포함함을 주의)
        logger.log('info', len(threading.enumerate()))
        while len(threading.enumerate()) > 10:
            logger.log('info', 'Waiting for other threads, 10sec.')
            time.sleep(10)

        ## 쓰레드 시작
        logger.log('info', 'Parsing thread will start for "%s". ' % (uid))
        x.start()

        ## 서버 부담을 덜기 위한 기다림
        logger.log('info', 'Waiting... 15sec')
        time.sleep(15)

    ## 전역 에러 처리 ##
    except parser_exceptions.LoginRequired as lre:
        print(lre)
        traceback.print_exc()
    except parser_exceptions.NoPaperDataError as npde:
        print(npde)
        traceback.print_exc()
    except Exception as e:
        print(e)
        traceback.print_exc()
    else:
        logger.log('info', 'Parsing %s ends' % (uid))
Ejemplo n.º 4
0
    except parser_exceptions.LoginRequired as lre:
        print(lre)
        traceback.print_exc()
    except parser_exceptions.NoPaperDataError as npde:
        print(npde)
        traceback.print_exc()
    except Exception as e:
        print(e)
        traceback.print_exc()
    else:
        logger.log('info', 'Parsing %s ends' % (uid))

    ## 전역 에러 끝 ##
    # 로직 끝 #


# 메세지 콜백 정의 끝

# 서버 메인
if __name__ == "__main__":
    # RabbitMQ 커넥션 설정 #
    connection = pika.BlockingConnection(
        pika.URLParameters(RABBITMQ_SERVER_URL))
    channel = connection.channel()

    channel.basic_consume('targetURLs', cons_callback)

    # 메세지 소비 시작 #
    Logger().log('info', 'Waiting for targetURLs. To exit press CTRL+C')
    channel.start_consuming()
# 서버 메인 끝
Ejemplo n.º 5
0
def parse_cite_list(soup: BeautifulSoup, session: requests.Session, uid: str):
    logger = Logger()  # 로기
    link = ''  # 추가 연도별 tc data가 있는 페이지 링크
    tc_data = {}  # 연도별 tc data

    # tc_paper_data   = []

    # TAG 정의 #

    TAG = {
        'PUB_YEAR_LINK': 'a#PublicationYear',
        'PUB_YEARS': 'div#PublicationYear_tr'
    }

    # TAG 정의 끝 #

    # 예외 페이지 처리 #
    if (re.search(r'None of the Citing Articles are in your subscription',
                  soup.text, re.I)):
        raise exceptions.CiteListNoSubsError()
    # 예외 페이지 처리 끝 #

    # FAST5000 데이터 다운로드 #
    SID = soup.select('input#SID')[0].attrs['value']
    qid = soup.select('input#qid')[0].attrs['value']
    rurl = soup.select('input#rurl')[0].attrs['value']

    action_url = '/OutboundService.do?action=go&&'
    form_data = {
        'qid': str(qid),
        'SID': SID,
        'mark_to': '5000',
        'markTo': '5000',
    }
    form_data = parser_constants.get_form_data(action_url, form_data)

    url = parser_constants.WOS_BASE_URL + action_url
    http_res = session.post(url, form_data)
    # FAST5000 데이터 다운로드 끝 #

    # FAST5000 데이터 정제 #
    fast_5000 = http_res.content.decode('utf-8').replace('\r', '')
    fast_5000_list = fast_5000.split('\n')
    keys = fast_5000_list[0].split('\t')
    fast_5000_list = fast_5000_list[1:]
    if fast_5000_list[-1] == '': fast_5000_list.pop()

    articles = []
    for row in fast_5000_list:
        article = {}

        row_list = row.split('\t')
        for idx, key in enumerate(keys):
            article[key.lower()] = row_list[idx]

        published_year = article['py'].strip() if article['py'] else 'unknown'
        published_date = article['pd'].strip(
        ) if article['pd'] else published_year
        published_month = published_date.split()[0]

        if published_year in tc_data:
            tc_data[published_year]['total'] += 1
        else:
            tc_data[published_year] = {'total': 1}

        if published_month in tc_data[published_year]:
            tc_data[published_year][published_month] += 1
        else:
            tc_data[published_year][published_month] = 1

        articles.append(article)

    # FAST5000 데이터 정제 끝 #

    # # 더 많은 연도를 조회할 필요가 있는 지 확인 #
    # logger.log('info', 'CITE_LIST//[%s] 더 많은 연도를 조회할 필요가 있는 지 확인' % uid)
    # pub_year_link = soup.select_one(TAG['PUB_YEAR_LINK'])

    # if pub_year_link:
    #     link = pub_year_link['href']
    #     return link, tc_data
    # # 더 많은 연도를 조회할 필요가 있는 지 확인 끝 #

    # pub_year_div = soup.select_one(TAG['PUB_YEARS'])

    # regex_cnt_by_year = r'([1-9][0-9]{3}) \(([0-9]+)\)'
    # ms = re.findall(regex_cnt_by_year, pub_year_div.text)

    # for tub in ms:
    #     tc_data[tub[0]] = int(tub[1])

    # logger.log('info', 'CITE_LIST//[%s] DONE' % uid)
    return link, tc_data, articles
Ejemplo n.º 6
0
def parse_detail(soup: BeautifulSoup, uid: str):
    logger = Logger()  # 로거
    ret_link: str = ''  # 대상을 인용 중인 논문 목록 링크
    paper_data: dict = {  # 논문 정보 
        'uid': uid,  # 논문 아이디
        'timesCited': 0,  # 인용 횟수
        'parsedAuthorList': [],  # 저자 리스트
        'firstAuthor': {},  # 제 1 저자
        'reprint': {},  # 교신 저자
        'journalImpact': {},  # 저널 랭크
        'grades': [],  # 논문 등급
        'recordState': '',  # 레코드 상태
    }
    # parsedAuthorList 원소 형태
    '''
        author = {
            'name'      : ''
            'fullName' : '',
            'address' : ['', '']
        }
    '''
    # grades 원소 형태
    '''
    {
        'fullGrade': '', 
        'caped': ''
    }
    '''

    # Tags 정의 #
    TAG = {
        'PAGING_BTN': 'a.paginationNext',
        'CITE_CNT_LINK': 'a.snowplow-citation-network-times-cited-count-link',
        'RECORD_INFOS': 'div.block-record-info',
        'SOURCE_TITLE': 'source_title_txt_label',
        'JCR_BLOCK': 'div.overlayJCRblock',
        'IF_TABLE': 'table.Impact_Factor_table',
        'JCR_TABLE': 'table.JCR_Category_table',
        'JCR_TEXT': 'p.overlayJCRtext',
        'GRADES': 'div.flex-justify-start > ul > span.box-label',
    }
    # Tags 정의 끝 #

    # 예외 페이지 처리 #
    logger.log('info', 'DETAIL//[%s] 예외 페이지 처리' % uid)
    pbtn = soup.select(TAG['PAGING_BTN'])

    if not pbtn and re.search('Record not available', soup.text, re.I):
        raise exceptions.RecordNotAvailableError()

    if not pbtn and re.search('server error', soup.text, re.I):
        raise exceptions.RecordNotAvailableError()

    if not pbtn and re.search('Access denied', soup.text, re.I):
        raise exceptions.AcessDeniedError()

    if not pbtn:
        raise exceptions.NoPaperDataError()
    # 예외 페이지 처리 끝 #

    # 인용 횟수 및 링크 #
    logger.log('info', 'DETAIL//[%s] 인용 횟수 및 링크' % uid)
    cnt_link = soup.select_one(TAG['CITE_CNT_LINK'])

    if not cnt_link:
        ret_link = ''
        paper_data['timesCited'] = 0
    else:
        ret_link = cnt_link['href']
        paper_data['timesCited'] = int(cnt_link.span.text)
    # 인용 횟수 및 링크 끝 #

    # 저자 및 주소 #
    logger.log('info', 'DETAIL//[%s] 저자 및 주소' % uid)
    authors = []
    reprint_name = ''
    address_map = {}

    raw_author_by = ''
    raw_author_infos = ''

    record_infos = soup.select(TAG['RECORD_INFOS'])

    target_flag = 0
    for info in record_infos:
        if target_flag is 2:
            break

        # 저자
        if info.text.find('By:') != -1:
            target_flag += 1
            raw_author_by = info.text.split(':')[1]

        # 주소
        if info.text.find('Author Information') != -1:
            target_flag += 1
            raw_author_infos = info.text

    regex_sp = r'\n|(&nbsp)|(  )|(\\xa0)|(\t)'
    raw_author_by = re.sub(regex_sp, '', raw_author_by, flags=re.M)
    raw_author_infos = re.sub(regex_sp, '', raw_author_infos, flags=re.M)

    ## 원본 주소 데이터 정제 ##
    logger.log('info', 'DETAIL//[%s] 원본 주소 데이터 정제' % uid)
    ptn_reprint = re.compile(
        r'.+Reprint Address:+(?P<name>[A-Z,a-z.\'\- ]+)\(reprint author\)(?P<address>[A-Z,a-z.\'\- 0-9&]+)?'
    )
    ptn_address = re.compile(
        r'\[ (?P<address_key>\d+) \] (?P<address>[A-Za-z,.\'\- 0-9&]+)')

    m = ptn_reprint.match(raw_author_infos)

    if m:
        reprint_name = m.group('name').strip() if m.group('name') else None
        reprint_address = m.group('address').strip() if m.group(
            'address') else None
    else:
        reprint_name = None
        reprint_address = None

    for address_tup in ptn_address.findall(raw_author_infos):
        address_map[address_tup[0].strip()] = address_tup[1].strip()

    ## 원본 주소 데이터 정제 끝 ##

    ## 원본 저자 데이터 정제 ##
    logger.log('info', 'DETAIL//[%s] 원본 저자 데이터 정제' % uid)
    '''By:Zhou, B (Zhou, Bo)[1 ]; Xu, YP (Xu, Yanping)[2 ]; Lee, SK (Lee, Seul Ki)[3,4 ]'''
    ptn_author_line = re.compile(
        r'(?P<name>[A-Z,a-z.\'\- ]+) (\((?P<full_name>[A-Z,a-z.\'\- ]+)\))?(\[(?P<address_keys>[0-9,\' ]+)\])?'
    )

    for raw_author in raw_author_by.split(';'):
        m = ptn_author_line.match(raw_author.strip())
        author = {
            'name': m.group('name').strip(),
            'fullName':
            m.group('full_name').strip() if m.group('full_name') else '',
            'address': [],
        }

        address_keys = m.group('address_keys')

        if not address_keys:
            address_keys = ['1'] if address_map else []
        else:
            address_keys = address_keys.split(',')

        for key in address_keys:
            key = key.strip()
            author['address'].append(address_map[key])

        # 교신 저자 정보가 없을 경우, 제 1저자가 교신 저자이다.
        if reprint_name and reprint_name == author['name']:
            if not author['address'] and reprint_address:
                author['address'] += [reprint_address]

            paper_data['reprint'] = author

        authors.append(author)

    if not reprint_name and authors:
        paper_data['reprint'] = authors[0]

    # paper_data['reprint']           = paper_data['reprint']['name']
    paper_data['firstAuthor'] = authors[0]
    paper_data['parsedAuthorList'] = authors
    ## 원본 저자 데이터 정제 끝 ##
    # 저자 및 주소 끝 #

    # JCR #
    sourceTitle = soup.select(TAG['SOURCE_TITLE'])
    jcr_block = soup.select(TAG['JCR_BLOCK'])
    journalImpact = {
        'sourceTitle': sourceTitle[0].text.strip() if sourceTitle else None,
        'impactFactorByYear': {},
        'jcrDataByYear': {}
    }

    if_table = None
    jcr_tables = None
    jcr_texts = None
    if jcr_block:
        if_table = soup.select(TAG['IF_TABLE'])
        jcr_tables = soup.select(TAG['JCR_TABLE'])
        jcr_texts = soup.select(TAG['JCR_TEXT'])

    if if_table:
        if_table = if_table[0]

        tds = if_table.find_all('td')
        ths = if_table.find_all('th')
        for idx in range(len(ths)):
            td = tds[idx]
            th = ths[idx]
            journalImpact['impactFactorByYear'][
                th.text.strip()] = td.text.strip()

    if jcr_tables:
        for idx, jcr_table in enumerate(jcr_tables):
            year = re.search(r'\d+', jcr_texts[idx].text)
            year = year[0] if year else 'unknown'

            journalImpact['jcrDataByYear'][year] = {
                'category': [],
                'rankInCategory': [],
                'quartileInCategory': []
            }

            trs = jcr_table = jcr_table.find_all('tr')
            for tr in trs[1:]:
                tds = tr.find_all('td')
                if tds[0]:
                    journalImpact['jcrDataByYear'][year]['category'].append(
                        tds[0].text.strip())
                if tds[1]:
                    journalImpact['jcrDataByYear'][year][
                        'rankInCategory'].append(tds[1].text.strip())
                if tds[2]:
                    journalImpact['jcrDataByYear'][year][
                        'quartileInCategory'].append(tds[2].text.strip())

    paper_data['journalImpact'] = journalImpact
    # JCR 끝 #

    # 등급 #
    logger.log('info', 'DETAIL//[%s] 등급' % uid)
    grades = []

    labels = soup.select(TAG['GRADES'])

    for label in labels:
        full_grade = label.text.replace('- ', '').strip()
        caped = re.sub(r'[ a-z]+', r'', full_grade)

        # grades += [{'fullGrade': full_grade, 'caped': caped}]
        grades += [caped]

    paper_data['grades'] = grades
    # 등급 끝 #

    logger.log('info', 'DETAIL//[%s] DONE' % uid)
    return (ret_link, paper_data)
Ejemplo n.º 7
0
 def __init__(self, parser_id=None):
     self.parser_id = parser_id if parser_id else os.getpid()
     self.es_time = 0
     self.logger = Logger()
     self.connection = None
     self.channel = None
Ejemplo n.º 8
0
def parse_detail(soup: BeautifulSoup, uid: str):
    logger = Logger()  # 로거
    ret_link: str = ''  # 대상을 인용 중인 논문 목록 링크
    paper_data: dict = {  # 논문 정보 
        'uid': uid,  # 논문 아이디
        'timesCited': 0,  # 인용 횟수
        'authors': [],  # 저자 리스트
        'firstAuthor': {},  # 제 1 저자
        'reprint': {},  # 교신 저자
        'jcr': {},  # 저널 랭크
        'grades': [],  # 논문 등급
        'recordState': ''  # 레코드 상태
    }
    # authors 원소 형태
    '''
        author = {
            'name'      : ''
            'fullName' : '',
            'addresses' : ['', '']
        }
    '''
    # grades 원소 형태
    '''
    {
        'fullGrade': '', 
        'caped': ''
    }
    '''

    # Tags 정의 #
    TAG = {
        'PAGING_BTN': 'a.paginationNext',
        'CITE_CNT_LINK': 'a.snowplow-citation-network-times-cited-count-link',
        'RECORD_INFOS': 'div.block-record-info',
        'JCR_TABLE': 'table.JCR_Category_table',
        'GRADES': 'div.flex-justify-start > ul > span.box-label',
    }
    # Tags 정의 끝 #

    # 예외 페이지 처리 #
    logger.log('info', 'DETAIL//[%s] 예외 페이지 처리' % uid)
    pbtn = soup.select(TAG['PAGING_BTN'])

    if not pbtn and re.search('Record not available', soup.text, re.I):
        raise exceptions.RecordNotAvailableError()

    if not pbtn and re.search('server error', soup.text, re.I):
        raise exceptions.RecordNotAvailableError()

    if not pbtn and re.search('Access denied', soup.text, re.I):
        raise exceptions.AcessDeniedError()

    if not pbtn:
        raise exceptions.NoPaperDataError()
    # 예외 페이지 처리 끝 #

    # 인용 횟수 및 링크 #
    logger.log('info', 'DETAIL//[%s] 인용 횟수 및 링크' % uid)
    cnt_link = soup.select_one(TAG['CITE_CNT_LINK'])

    if not cnt_link:
        ret_link = ''
        paper_data['timesCited'] = 0
    else:
        ret_link = cnt_link['href']
        paper_data['timesCited'] = int(cnt_link.span.text)
    # 인용 횟수 및 링크 끝 #

    # 저자 및 주소 #
    logger.log('info', 'DETAIL//[%s] 저자 및 주소' % uid)
    authors = []
    reprint_name = ''
    address_map = {}

    raw_author_by = ''
    raw_author_infos = ''

    record_infos = soup.select(TAG['RECORD_INFOS'])

    target_flag = 0
    for info in record_infos:
        if target_flag is 2:
            break

        # 저자
        if info.text.find('By:') != -1:
            target_flag += 1
            raw_author_by = info.text.split(':')[1]

        # 주소
        if info.text.find('Author Information') != -1:
            target_flag += 1
            raw_author_infos = info.text

    regex_sp = r'\n|(&nbsp)|(  )|(\\xa0)|(\t)'
    raw_author_by = re.sub(regex_sp, '', raw_author_by, flags=re.M)
    raw_author_infos = re.sub(regex_sp, '', raw_author_infos, flags=re.M)

    ## 원본 주소 데이터 정제 ##
    logger.log('info', 'DETAIL//[%s] 원본 주소 데이터 정제' % uid)
    ptn_reprint = re.compile(
        r'.+Reprint Address:+(?P<name>[A-Z,a-z.\'\- ]+)\(reprint author\)(?P<address>[A-Z,a-z.\'\- 0-9&]+)?'
    )
    ptn_address = re.compile(
        r'\[ (?P<address_key>\d+) \] (?P<address>[A-Za-z,.\'\- 0-9&]+)')

    m = ptn_reprint.match(raw_author_infos)

    if m:
        reprint_name = m.group('name').strip() if m.group('name') else None
        reprint_address = m.group('address').strip() if m.group(
            'address') else None
    else:
        reprint_name = None
        reprint_address = None

    for address_tup in ptn_address.findall(raw_author_infos):
        address_map[address_tup[0].strip()] = address_tup[1].strip()

    ## 원본 주소 데이터 정제 끝 ##

    ## 원본 저자 데이터 정제 ##
    logger.log('info', 'DETAIL//[%s] 원본 저자 데이터 정제' % uid)
    '''By:Zhou, B (Zhou, Bo)[1 ]; Xu, YP (Xu, Yanping)[2 ]; Lee, SK (Lee, Seul Ki)[3,4 ]'''
    ptn_author_line = re.compile(
        r'(?P<name>[A-Z,a-z.\'\- ]+) (\((?P<full_name>[A-Z,a-z.\'\- ]+)\))?(\[(?P<address_keys>[0-9,\' ]+)\])?'
    )

    for raw_author in raw_author_by.split(';'):
        m = ptn_author_line.match(raw_author.strip())
        author = {
            'name': m.group('name').strip(),
            'fullName':
            m.group('full_name').strip() if m.group('full_name') else '',
            'addresses': [],
            'reprint': False
        }

        address_keys = m.group('address_keys')

        if not address_keys:
            address_keys = ['1'] if address_map else []
        else:
            address_keys = address_keys.split(',')

        for key in address_keys:
            key = key.strip()
            author['addresses'].append(address_map[key])

        # 교신 저자 정보가 없을 경우, 제 1저자가 교신 저자이다.
        if reprint_name and reprint_name == author['name']:
            if not author['addresses'] and reprint_address:
                author['addresses'] += [reprint_address]

            author['reprint'] = True
            paper_data['reprint'] = author

        authors.append(author)

    if not reprint_name and authors:
        authors[0]['reprint'] = True
        paper_data['reprint'] = authors[0]

    paper_data['firstAuthor'] = authors[0]
    paper_data['authors'] = authors
    ## 원본 저자 데이터 정제 끝 ##
    # 저자 및 주소 끝 #

    # JCR #
    # 일단 생략한다.
    # JCR 끝 #

    # 등급 #
    logger.log('info', 'DETAIL//[%s] 등급' % uid)
    grades = []

    labels = soup.select(TAG['GRADES'])

    for label in labels:
        full_grade = label.text.replace('- ', '').strip()
        caped = re.sub(r'[ a-z]+', r'', full_grade)

        grades += [{'fullGrade': full_grade, 'caped': caped}]

    paper_data['grades'] = grades
    # 등급 끝 #

    logger.log('info', 'DETAIL//[%s] DONE' % uid)
    return (ret_link, paper_data)