diff --git a/networkx/readwrite/gml.py b/networkx/readwrite/gml.py
index d9e49d336..d3c8b8527 100644
--- a/networkx/readwrite/gml.py
+++ b/networkx/readwrite/gml.py
@@ -286,7 +286,7 @@ def parse_gml_lines(lines, label, destringizer):
     """
     def tokenize():
         patterns = [
-            r'[A-Za-z][0-9A-Za-z]*\s+',  # keys
+            r'[A-Za-z][0-9A-Za-z_]*\s+',  # keys
             r'[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)(?:[Ee][+-]?[0-9]+)?',  # reals
             r'[+-]?[0-9]+',   # ints
             r'".*?"',         # strings

Cannot parse GML files from Cytoscape, possible regression?
This does not appear to be a duplicate of #321 -- I cannot parse .GML files produced from Cytoscape, including the "simple.gml" [file](https://networkx.lanl.gov/trac/attachment/ticket/324/simple.gml) as reported in #321. Below is the output I receive if parsing the file provided in that issue. 

From reading the comments, it looks like the suggestion is to either modify the GML parser directly in networkx (allowing slightly out of format GML files), or to modify the output from Cytoscape. If I issue a PR for the GML parser modification, would there be support for it?

```python
10:20:28 (mcdonadt@8086):~> nx.__version__
Out[5]: '1.10'

10:20:32 (mcdonadt@8086):~> g = nx.read_gml('Downloads/simple.gml')
---------------------------------------------------------------------------
NetworkXError                             Traceback (most recent call last)
<ipython-input-6-e05f5232526f> in <module>()
----> 1 g = nx.read_gml('Downloads/simple.gml')

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in read_gml(path, label, destringizer)

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/utils/decorators.py in _open_file(func, *args, **kwargs)
    218         # Finally, we call the original function, making sure to close the fobj.
    219         try:
--> 220             result = func(*new_args, **kwargs)
    221         finally:
    222             if close_fobj:

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in read_gml(path, label, destringizer)
    208             yield line
    209
--> 210     G = parse_gml_lines(filter_lines(path), label, destringizer)
    211     return G
    212

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in parse_gml_lines(lines, label, destringizer)
    381
    382     tokens = tokenize()
--> 383     graph = parse_graph()
    384
    385     directed = graph.pop('directed', False)

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in parse_graph()
    370
    371     def parse_graph():
--> 372         curr_token, dct = parse_kv(next(tokens))
    373         if curr_token[0] is not None:  # EOF
    374             unexpected(curr_token, 'EOF')

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in parse_kv(curr_token)
    355                 curr_token = next(tokens)
    356             elif type == 4:  # dict start
--> 357                 curr_token, value = parse_dict(curr_token)
    358             else:
    359                 unexpected(curr_token, "an int, float, string or '['")

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in parse_dict(curr_token)
    365     def parse_dict(curr_token):
    366         curr_token = consume(curr_token, 4, "'['")    # dict start
--> 367         curr_token, dct = parse_kv(curr_token)
    368         curr_token = consume(curr_token, 5, "']'")  # dict end
    369         return curr_token, dct

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in parse_kv(curr_token)
    355                 curr_token = next(tokens)
    356             elif type == 4:  # dict start
--> 357                 curr_token, value = parse_dict(curr_token)
    358             else:
    359                 unexpected(curr_token, "an int, float, string or '['")

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in parse_dict(curr_token)
    364
    365     def parse_dict(curr_token):
--> 366         curr_token = consume(curr_token, 4, "'['")    # dict start
    367         curr_token, dct = parse_kv(curr_token)
    368         curr_token = consume(curr_token, 5, "']'")  # dict end

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in consume(curr_token, type, expected)
    334     def consume(curr_token, type, expected):
    335         if curr_token[0] == type:
--> 336             return next(tokens)
    337         unexpected(curr_token, expected)
    338

/Users/mcdonadt/miniconda3/envs/unifrac-network/lib/python3.4/site-packages/networkx/readwrite/gml.py in tokenize()
    321                 else:
    322                     raise NetworkXError('cannot tokenize %r at (%d, %d)' %
--> 323                                         (line[pos:], lineno + 1, pos + 1))
    324             lineno += 1
    325         yield (None, None, lineno + 1, 1)  # EOF

NetworkXError: cannot tokenize 'root_index\t-3' at (5, 3)
```
diff --git a/networkx/readwrite/tests/test_gml.py b/networkx/readwrite/tests/test_gml.py
index 85b069127..042061032 100644
--- a/networkx/readwrite/tests/test_gml.py
+++ b/networkx/readwrite/tests/test_gml.py
@@ -65,6 +65,90 @@ graph [
   ]
 ]
 """
+    def test_parse_gml_cytoscape_bug(self):
+        # example from issue #321, originally #324 in trac
+        cytoscape_example = """
+Creator "Cytoscape"
+Version 1.0
+graph   [
+    node    [
+        root_index  -3
+        id  -3
+        graphics    [
+            x   -96.0
+            y   -67.0
+            w   40.0
+            h   40.0
+            fill    "#ff9999"
+            type    "ellipse"
+            outline "#666666"
+            outline_width   1.5
+        ]
+        label   "node2"
+    ]
+    node    [
+        root_index  -2
+        id  -2
+        graphics    [
+            x   63.0
+            y   37.0
+            w   40.0
+            h   40.0
+            fill    "#ff9999"
+            type    "ellipse"
+            outline "#666666"
+            outline_width   1.5
+        ]
+        label   "node1"
+    ]
+    node    [
+        root_index  -1
+        id  -1
+        graphics    [
+            x   -31.0
+            y   -17.0
+            w   40.0
+            h   40.0
+            fill    "#ff9999"
+            type    "ellipse"
+            outline "#666666"
+            outline_width   1.5
+        ]
+        label   "node0"
+    ]
+    edge    [
+        root_index  -2
+        target  -2
+        source  -1
+        graphics    [
+            width   1.5
+            fill    "#0000ff"
+            type    "line"
+            Line    [
+            ]
+            source_arrow    0
+            target_arrow    3
+        ]
+        label   "DirectedEdge"
+    ]
+    edge    [
+        root_index  -1
+        target  -1
+        source  -3
+        graphics    [
+            width   1.5
+            fill    "#0000ff"
+            type    "line"
+            Line    [
+            ]
+            source_arrow    0
+            target_arrow    3
+        ]
+        label   "DirectedEdge"
+    ]
+]
+"""
+        nx.parse_gml(cytoscape_example)
 
     def test_parse_gml(self):
         G = nx.parse_gml(self.simple_data, label='label')

pip install -e .
parse_log_pytest
requirements.txt
nose pytest
apt-get update apt-get install -y libgdal-dev graphviz
3.6
requirements/default.txt requirements/test.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
decorator==5.1.1
importlib-metadata==4.8.3
iniconfig==1.1.1
-e git+https://github.com/networkx/networkx.git@328d899fb17bac857eedd9ec3e03f4ede9b11c63#egg=networkx
nose==1.3.7
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: networkx
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - decorator==5.1.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - nose==1.3.7
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/networkx

networkx/readwrite/tests/test_gml.py::TestGraph::test_parse_gml_cytoscape_bug
networkx/readwrite/tests/test_gml.py::TestGraph::test_parse_gml networkx/readwrite/tests/test_gml.py::TestGraph::test_read_gml
networkx/readwrite/tests/test_gml.py::TestGraph::test_relabel_duplicate networkx/readwrite/tests/test_gml.py::TestGraph::test_tuplelabels networkx/readwrite/tests/test_gml.py::TestGraph::test_quotes networkx/readwrite/tests/test_gml.py::TestGraph::test_name networkx/readwrite/tests/test_gml.py::TestGraph::test_graph_types networkx/readwrite/tests/test_gml.py::TestGraph::test_data_types networkx/readwrite/tests/test_gml.py::TestGraph::test_escape_unescape networkx/readwrite/tests/test_gml.py::TestGraph::test_exceptions
networkx/readwrite/gml.py
networkx/readwrite/gml.py