import re
import sys
import os
import pprint

import click

try:
    import shodan
except ImportError:
    print('Install shodan: pip install shodan')
    sys.exit(0)


SHODAN_API_KEY = os.environ['SHODAN_API_KEY']


def get_headers(text):
    header_string = re.findall(
        '^(.*?)(?:[\r\n]{3,4})', text, flags=re.DOTALL | re.I
    )
    if not header_string:
        return None

    data = {}
    for line in header_string[0].splitlines():
        match = re.findall('^(.*?):(.*)', line)

        if match:
            key, value = map(lambda v: v.strip(), match[0])
            data[key] = value

    return data


@click.command()
@click.option(
    '--filter',
    default=None,
    type=str,
    help='Filter by header'
)
@click.option(
    '--stats',
    default=False,
    is_flag=True,
    help='Include stats'
)
@click.argument('query')
def main(filter, stats, query):
    counter = 0
    filtered_header = []
    api = shodan.Shodan(SHODAN_API_KEY)

    try:
        result = api.search(query)
    except shodan.exception.APIError:
        print('[-] API connection error.')
        sys.exit(0)

    for match in result['matches']:
        hd = get_headers(match['data'])
        if not hd:
            continue

        if filter:
            value = hd.get(filter)
            if value:
                filtered_header.append(value)
        else:
            pprint.pprint(hd, width=160)
            counter += 1

    if filtered_header:
        pprint.pprint(filtered_header, width=160)

    if stats:
        print()
        print('[+] n_matches: {}'.format(len(result['matches'])))
        print('[+] n_printed: {}'.format(counter or len(filtered_header)))


if __name__ == '__main__':
    main()

import re
import sys
import os
import pprint

import click

try:
    import shodan
except ImportError:
    print('Install shodan: pip install shodan')
    sys.exit(0)

try:
    SHODAN_API_KEY = os.environ['SHODAN_API_KEY']
except KeyError:
    print('Set SHODAN_API_KEY environment variable with your key')
    sys.exit(0)


def get_headers(text):
    header_string = re.findall(
        '^(.*?)(?:[\r\n]{3,4})', text, flags=re.DOTALL | re.I
    )
    if not header_string:
        return None

    data = {}
    for line in header_string[0].splitlines():
        match = re.findall('^(.*?):(.*)', line)

        if match:
            key, value = map(lambda v: v.strip(), match[0])
            data[key] = value

    return data


@click.command()
@click.option(
    '--filter',
    default=None,
    type=str,
    help='Filter by header'
)
@click.option(
    '--stats',
    default=False,
    is_flag=True,
    help='Include stats'
)
@click.option(
    '--show-names',
    default=False,
    is_flag=True,
    help='Show header names'
)
@click.argument('query')
def main(filter, stats, show_names, query):
    counter = 0
    filtered_header = set()
    api = shodan.Shodan(SHODAN_API_KEY)

    try:
        result = api.search(query)
    except shodan.exception.APIError:
        print('[-] API connection error.')
        sys.exit(0)

    for match in result['matches']:
        server = '{}:{}'.format(match['ip_str'], match['port'])
        hd = get_headers(match['data'])
        if not hd:
            continue

        if show_names:
            filtered_header.update(set(hd.keys()))
        elif filter:
            value = hd.get(filter)
            if value:
                filtered_header.add((server, value))
        else:
            pprint.pprint(hd, width=160)
            counter += 1

    if filtered_header:
        pprint.pprint(filtered_header, width=160)

    if stats:
        print('\n--- Stats ---')
        print('[+] n_matches: {}'.format(len(result['matches'])))
        print('[+] n_printed: {}'.format(counter or len(filtered_header)))


if __name__ == '__main__':
    main()

Improve shodan script with option to show header names and better output